dmaicher / doctrine-test-bundle

Symfony bundle to isolate your app's doctrine database tests and improve the test performance
MIT License
1.08k stars 60 forks source link

DB is not empty between two test files #223

Closed GlucNAc closed 2 years ago

GlucNAc commented 2 years ago

Hello :)

The problem I'm facing is that my DB is not empty when my second test is running. I have tried many things to understand the problem and to make it reproducible.

I'm sure I'm doing something wrong somewhere, but I don't know if it is a matter of setting up Foundry or DAMA, or maybe both.

Let's first talk about the environment :

I have copied an adapted the test file and its trait from DAMA Bundle : https://gist.github.com/GlucNAc/7f7986f2303f67d519b67818370aff70.

If I run the file PhpunitTest (from the above gist) or Phpunit2Test on their own, it works fine. But if I run them both subsequently, then the second one fails :

image

As you can see here testChangeDbState() of the subsequent test performs $this->assertRowCount(0) which actually returns 1.

What I don't understand is why the methods inside a unique file seem to be independent, but not from file to file ?

If this assertion is false, then it seems a commit has been done to the DB. If we take look at the code we can see there are lines that perform a commit :

But even if I remove the corresponding methods, the problem is still here. And these lines bring me to another question :

image

Line 63 a commit is done, meaning data have been written in the DB (right?). So why the subsequent assertion line 72 does not fail then? I think the answer of this question is one of the key to understand what am I doing wrong ^^

dmaicher commented 2 years ago

Does it work without using Zenstruck\Foundry\Test\ResetDatabase?

dmaicher commented 2 years ago

Line 63 a commit is done, meaning data have been written in the DB (right?). So why the subsequent assertion line 72 does not fail then? I think the answer of this question is one of the key to understand what am I doing wrong ^^

Doctrine supports nested transactions via SAVEPOINTs. So this test makes sure rolling back a nested transaction (that is wrapped by some outer transaction of the bundle) works.

GlucNAc commented 2 years ago

Thanks for your answer :)

Does it work without using Zenstruck\Foundry\Test\ResetDatabase?

I have removed Foundry bundle using composer and I have adapted my tests/bootstrap.php file in order to reset the DB and create the table and its field to make the bundle working.

As previously described, the first test file is OK but not the second one since it first method.

GlucNAc commented 2 years ago

UPDATE : I have setting up a new SF 6.1 project which reproduces the bug : https://gitlab.com/GlucNAc/dama-bug. Please read README file.

dmaicher commented 2 years ago

Thanks. I will try to find some time and have a look.

dmaicher commented 2 years ago

The last test cases are actually testing that the transactional behavior can be disabled and thus the row will be actually persisted.

Like this the tests all pass:

diff --git a/tests/PhpunitTest.php b/tests/PhpunitTest.php
index b2e5eb2..4b33123 100644
--- a/tests/PhpunitTest.php
+++ b/tests/PhpunitTest.php
@@ -120,6 +120,18 @@ class PhpunitTest extends KernelTestCase
      */
     public function testChangesFromPreviousTestAreVisibleWhenDisabledDuringRuntime(): void
     {
+        StaticDriver::setKeepStaticConnections(false);
+
+        self::ensureKernelShutdown();
+        $this->setUp();
+
         $this->assertRowCount(1);
+
+        // cleanup persisted rows to not affect any other tests afterwards
+        $this->connection->executeQuery('DELETE FROM question');
+
+        $this->assertRowCount(0);
+
+        StaticDriver::setKeepStaticConnections(true);
     }
 }

I now added a cleanup for this case: https://github.com/dmaicher/doctrine-test-bundle/pull/225

GlucNAc commented 1 year ago

Hi @dmaicher, sorry for not having back to you soon!

Thanks for your fix! Actually, I finally found why I was having this issue. It was because my tests are relying on pk, which are not reset on rollback (obviously). So having an id != 1 when creating an object leads me to thought db was not empty, whereas it wasn't...

So to make my integration tests working OK, I call the following method when needed:

public static function resetAllPk (bool $runAsStandAlone = true): void
{
    if ($runAsStandAlone) {
        self::bootKernel();

        if ($isDAMADoctrineTestBundleEnabled = DatabaseResetter::isDAMADoctrineTestBundleEnabled()) {
            // disable static connections for this operation
            StaticDriver::setKeepStaticConnections(false);
        }
    }

    $entityManager = self::$connection ?? self::$kernel->getContainer()->get('doctrine.orm.entity_manager');
    $connection = $entityManager->getConnection();

    $platform = $connection->getDatabasePlatform();
    foreach ($entityManager->getMetadataFactory()->getAllMetadata() as $class) {
        if ($class->isMappedSuperclass || $class->isEmbeddedClass) {
            continue;
        }

        $table = $entityManager->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform);
        $connection->executeStatement("ALTER TABLE $table AUTO_INCREMENT = 1");
    }

    if ($runAsStandAlone) {
        if ($isDAMADoctrineTestBundleEnabled) {
            // re-enable static connections
            StaticDriver::setKeepStaticConnections(true);
        }

        self::$kernel->shutdown();
    }
}

The code is not complete but you got the point I think.

As I know everything is OK now, I will rewrite these tests as unit ones instead of integration.

Thanks for your time :)