Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using factories in a KernelTestCase without Factories trait should throw #750

Open
cooldude77 opened this issue Dec 16, 2024 · 6 comments · May be fixed by #766
Open

Using factories in a KernelTestCase without Factories trait should throw #750

cooldude77 opened this issue Dec 16, 2024 · 6 comments · May be fixed by #766
Labels
enhancement New feature or request

Comments

@cooldude77
Copy link

cooldude77 commented Dec 16, 2024

Hi ,
I have created a test class . I use foundry methods to first create and then search for the created users and create connections between them. However I see that entities that are searched are not managed. I then need to "re-search" them in order for them to become managed.

<?php

namespace App\Tests\Repository;

use App\Entity\Connection;
use App\Entity\User;
use App\Factory\UserFactory;
use App\Repository\ConnectionRepository;
use App\Tests\Fixtures\UserFixtures;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class ConnectionRepositoryTest extends KernelTestCase
{
    private ?EntityManager $entityManager;
    private ConnectionRepository $repository;
    use UserFixtures;

    protected function setUp(): void
    {
        $kernel = self::bootKernel();

        $this->entityManager = $kernel->getContainer()
            ->get('doctrine')
            ->getManager();

        $this->repository = $this->entityManager->getRepository(Connection::class);
    }

    public function testDeleteConnection()
    {

    }

    public function testCreateConnection()
    {

        $this->createUsers();

        $a = $this->entityManager->contains($this->mainUser->_real());
        $b = $this->entityManager->contains(UserFactory::find(['id' => $this->mainUser->getId()])->_real());

        // need to
        $userA = $this->entityManager->getRepository(User::class)->find($this->mainUser->getId());
        $userB = $this->entityManager->getRepository(User::class)->find($this->users[0]->getId());

        $c = $this->entityManager->contains($userA);

        $this->repository->createConnection($userA, $userB);

        self::assertNotNull($this->repository->findOneBy(['user' => $userA, 'connectedToUser' => $userB]));
        self::assertNotNull($this->repository->findOneBy(['connectedToUser' => $userB, 'user' => $userA]));

    }
}

The fixture class is simple

<?php

namespace App\Tests\Fixtures;

use App\Factory\UserFactory;

trait UserFixtures
{
    public array $users;
    /**
     * @var \App\Entity\User|mixed|object|(object&\Zenstruck\Foundry\Persistence\Proxy)|\Zenstruck\Foundry\Persistence\Proxy
     */
    public mixed $mainUser;

    public function createUsers()
    {
        $this->mainUser = UserFactory::createOne(['roles' => ['ROLE_USER']]);

        $this->users = UserFactory::createMany(10, ['roles' => ['ROLE_USER']]);

    }
}

Here are the result of debugging while testing
image

If I pass _real() values to the repository like below

 public function testCreateConnection()
    {

        $this->createUsers();

        $a = $this->entityManager->contains($this->mainUser->_real());
        $b = $this->entityManager->contains(UserFactory::find(['id' => $this->mainUser->getId()])->_real());

        // need to
        $userA = $this->entityManager->getRepository(User::class)->find($this->mainUser->getId());
        $userB = $this->entityManager->getRepository(User::class)->find($this->users[0]->getId());

        $c = $this->entityManager->contains($userA);

        $this->repository->createConnection($this->mainUser->_real(), $this->users[0]->_real());

        self::assertNotNull($this->repository->findOneBy(['user' => $userA, 'connectedToUser' => $userB]));
        self::assertNotNull($this->repository->findOneBy(['connectedToUser' => $userB, 'user' => $userA]));

    }

I get the error
`

  • A new entity was found through the relationship 'App\Entity\Connection#connectedToUser' that was not configured to cascade persist operations for entity: App\Entity\User@818. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @manytoone(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'App\Entity\User#__toString()' to get a clue.
  • A new entity was found through the relationship 'App\Entity\Connection#user' that was not configured to cascade persist operations for entity: App\Entity\User@986. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @manytoone(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'App\Entity\User#__toString()' to get a clue.

`
If I pass entities passed after finding them thru entities , then the test passes
image

Is there a workaround to making entities managed upon searching using Foundry method ?

Thanks and regards
Ashvini

@nikophil
Copy link
Member

nikophil commented Dec 16, 2024

Hi!

that's really surprising, I've just tested and $em->contains($entity) returns true. Which is normal, because the entities are created thanks to the entity manager, and $em->clear() is not called. It seems it comes from your app 🤔

Sorry, but unless you manage to reproduce it in the Foundry's test suite, or create a public reproducer, I cannot help 🤷

@nikophil nikophil added the question Further information is requested label Dec 16, 2024
@cooldude77
Copy link
Author

Hi @nikophil ,

Thanks for your reply. I have created a test repo. Please download and install dependencies and then run ConnectionRepositoryTest.php in the tests/Repository folder. I have added working and non working creation of connection test cases. ( pref environment is ubuntu and LAMP stack)
Sorry if there is a bug from my side. I will try to correct and update it again if necessary.

Repo
Thanks again,
Ashvini

@nikophil
Copy link
Member

nikophil commented Dec 18, 2024

Hi @cooldude77

the problem comes from that you're not using the Factories trait.

I was expecting Foundry to throw an exception FoundryNotBooted, but it actually doesn't because self::bootKernel() is explicitely called and we're booting Foundry anyway, when the kernel is booted.

@kbond would you see a solution to this? We need to boot automatically Foundry for users who use it outside from the tests (for instances along with data fixtures bundle), but we should force the trait to be used as soon as we're in a PHPUnit context + KernelTestCase

@nikophil nikophil changed the title Created/Searched Entity is unmanaged Using factories in a KernelTestCase without Factories trait should throw Dec 18, 2024
@nikophil nikophil added the enhancement New feature or request label Dec 18, 2024
@cooldude77
Copy link
Author

Hey @nikophil

Yeah , a small change got it working . Thanks for your time. :)

@nikophil nikophil removed the question Further information is requested label Dec 18, 2024
@kbond
Copy link
Member

kbond commented Dec 18, 2024

Is there an elegant way to check if foundry is being used in a test without the trait? debug_backtrace is all I can think of...

@nikophil
Copy link
Member

nikophil commented Dec 18, 2024

few other ideas:
- check the existence of const PHPUNIT_COMPOSER_INSTALL
- check if $_SERVER['SCRIPT_NAME'] contains phpunit

ok then, the only relevant method I found is actually to use debug_backtrace() 🤷

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Development

Successfully merging a pull request may close this issue.

3 participants