Home » Php » php – How to test Doctrine Migrations?

php – How to test Doctrine Migrations?

Posted by: admin July 12, 2020 Leave a comment

Questions:

I’m working on a project that does NOT have a copy of production DB on development environment.

Sometimes we have an issue with DB migrations – they pass on dev DB but fail in production/testing.

It’s often beacuse Dev environent data is loaded from Fixtures that use the latest entities – filling all tables properly.

Is there any easy way to make sure Doctrine Migration(s) will pass in production?

Do you have/know any way to write an automatic tests that will make sure data will be migrated properly without downloading the production/testing DB and running the migration manually?

I would like to avoid downloading a production/testing DB to dev machine so I can check migrations becasue that DB contains private data and it can be quite big.

How to&Answers:

First, you need to create a sample database dump in state before the migration. For MySQL use mysqldump. For postgres pg_dump, e.g.:

mysqldump -u root -p mydatabase > dump-2018-02-20.sql
pg_dump -Upostgres --inserts --encoding utf8 -f dump-2018-02-20.sql mydatabase

Then create an abstract class for all migrations tests (I assume you have configured a separate database for integration testing in config_test.yml):

abstract class DatabaseMigrationTestCase extends WebTestCase {
    /** @var ResettableContainerInterface */
    protected $container;
    /** @var Application */
    private $application;

    protected function setUp() {
        $this->container = self::createClient()->getContainer();
        $kernel = $this->container->get('kernel');
        $this->application = new Application($kernel);
        $this->application->setAutoExit(false);
        $this->application->setCatchExceptions(false);

        $em = $this->container->get(EntityManagerInterface::class);
        $this->executeCommand('doctrine:schema:drop --force');
        $em->getConnection()->exec('DROP TABLE IF EXISTS public.migration_versions');
    }

    protected function loadDump(string $name) {
        $em = $this->container->get(EntityManagerInterface::class);
        $em->getConnection()->exec(file_get_contents(__DIR__ . '/dumps/dump-' . $name . '.sql'));
    }

    protected function executeCommand(string $command): string {
        $input = new StringInput("$command --env=test");
        $output = new BufferedOutput();
        $input->setInteractive(false);
        $returnCode = $this->application->run($input, $output);
        if ($returnCode != 0) {
            throw new \RuntimeException('Failed to execute command. ' . $output->fetch());
        }
        return $output->fetch();
    }

    protected function migrate(string $toVersion = '') {
        $this->executeCommand('doctrine:migrations:migrate ' . $toVersion);
    }
}

Example migration test:

class Version20180222232445_MyMigrationTest extends DatabaseMigrationTestCase {
    /** @before */
    public function prepare() {
        $this->loadDump('2018-02-20');
        $this->migrate('20180222232445');
    }

    public function testMigratedSomeData() {
        $em = $this->container->get(EntityManagerInterface::class);
        $someRow = $em->getConnection()->executeQuery('SELECT * FROM myTable WHERE id = 1')->fetch();
        $this->assertEquals(1, $someRow['id']);
        // check other stuff if it has been migrated correctly
    }
}

Answer:

I’ve figured out simple “smoke tests” for Doctrine Migrations.

I have PHPUnit test perfoming following steps:

  • Drop test DB
  • Create test DB
  • Load migrations (create schema)
  • Load fixtures (imitate production data)
  • Migrate to some older version
  • Migrate back to the latest version

This way I can test for the major issues, we’ve had recently.

Example of PHPUnit tests can be found on my blog: http://damiansromek.pl/2015/09/29/how-to-test-doctrine-migrations/