Testing django migrations
A number of times over the past few years I’ve needed to create some quite complex migrations (both schema and data) in a few of the Django apps that I help out with at Canonical. And like any TDD fanboy, I cry at the thought of deploying code that I’ve just tested by running it a few times with my own sample data (or writing code without first setting failing tests demoing the expected outcome).
This migration test case helper has enabled me to develop migrations test first:
class MigrationTestCase(TransactionTestCase): """A Test case for testing migrations.""" # These must be defined by subclasses. start_migration = None dest_migration = None django_application = None def setUp(self): super(MigrationTestCase, self).setUp() migrations = Migrations(self.django_application) self.start_orm = migrations[self.start_migration].orm() self.dest_orm = migrations[self.dest_migration].orm() # Ensure the migration history is up-to-date with a fake migration. # The other option would be to use the south setting for these tests # so that the migrations are used to setup the test db. call_command('migrate', self.django_application, fake=True, verbosity=0) # Then migrate back to the start migration. call_command('migrate', self.django_application, self.start_migration, verbosity=0) def tearDown(self): # Leave the db in the final state so that the test runner doesn't # error when truncating the database. call_command('migrate', self.django_application, verbosity=0) def migrate_to_dest(self): call_command('migrate', self.django_application, self.dest_migration, verbosity=0)
It’s not perfect – schema tests in particular end up being quite complicated as you need to ensure you’re working with the correct orm model when creating your test data – and you can’t use the normal factories to create your test data. But it does enable you to write migration tests like:
class MyMigrationTestCase(MigrationTestCase): start_migration = '0022_previous_migration' dest_migration = '0024_data_migration_after_0023_which_would_be_schema_changes' django_application = 'myapp' def test_schema_and_data_updated(self): # Test setup code self.migrate_to_dest() # Assertions
which keeps me happy. When I wrote that I couldn’t find any other suggestions out there for testing migrations. A quick search now turns up one idea from André (data-migrations only), but nothing else substantial. Let me know if you’ve seen something similar or a way to improve testing of migrations.