8
votes

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.

2
If you are using Git have you think to go back to the last prod version, recreate your db, fuxture, migration and go to the must up to date version to run the newer migration. However the only reliable test is unfortunately with prod data.Léo Benoist

2 Answers

4
votes

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
    }
}
1
votes

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/