Skip to main content

Test django schema and data migrations, including ordering

Project description

django-test-migrations

wemake.services Build Status Coverage Python Version wemake-python-styleguide

Features

  • Allows to test django schema and data migrations
  • Allows to test both forward and rollback migrations
  • Allows to test the migrations order
  • Fully typed with annotations and checked with mypy, PEP561 compatible
  • Easy to start: has lots of docs, tests, and tutorials

Installation

pip install django-test-migrations

We support several django versions:

  • 1.11
  • 2.1
  • 2.2

Other versions might work too, but they are not officially supported.

Testing django migrations

Testing migrations is not a frequent thing in django land. But, sometimes it is totally required. When?

When we do complex schema or data changes and what to be sure that existing data won't be corrupted. We might also want to be sure that all migrations can be safely rolled back. And as a final touch we want to be sure that migrations are in the correct order and have correct dependencies.

Testing forward migrations

To test all migrations we have a Migrator class.

It has three methods to work with:

  • .before() which takes app and migration names to generate a state before the actual migration happens
  • .after() which takes app and migration names to perform the actual migration
  • .reset() to clean everything up after we are done with testing

So, here's an example:

from django_test_migrations.migrator import Migrator

migrator = Migrator(database='default')

# Initial migration, currently our model has only a single string field:
old_state = migrator.before(('main_app', '0001_initial'))
SomeItem = old_state.apps.get_model('main_app', 'SomeItem')

# Let's create a model with just a single field specified:
SomeItem.objects.create(string_field='a')
assert len(SomeItem._meta.get_fields()) == 2  # id + string_field

# Now this migration will add `is_clean` field to the model:
new_state = migrator.after(('main_app', '0002_someitem_is_clean'))
SomeItem = new_state.apps.get_model('main_app', 'SomeItem')

# We can now test how our migration worked, new field is there:
assert SomeItem.objects.filter(is_clean=True).count() == 0
assert len(SomeItem._meta.get_fields()) == 3  # id + string_field + is_clean

# Cleanup:
migrator.reset()

That was an example of a forward migration.

Backward migration

The thing is that you can also test backward migrations. Nothing really changes except migration names that you pass and your logic:

migrator = Migrator()

# Currently our model has two field, but we need a rollback:
old_state = migrator.before(('main_app', '0002_someitem_is_clean'))
SomeItem = old_state.apps.get_model('main_app', 'SomeItem')

# Create some data to illustrate your cases:
# ...

# Now this migration will drop `is_clean` field:
new_state = migrator.after(('main_app', '0001_initial'))

# Assert the results:
# ...

# Cleanup:
migrator.reset()

Testing migrations ordering

Sometimes we also want to be sure that our migrations are in the correct order. And all our dependecies = [...] are correct.

To achieve that we have plan.py module.

That's how it can be used:

from django_test_migrations.plan import all_migrations, nodes_to_tuples

main_migrations = all_migrations('default', ['main_app', 'other_app'])
assert nodes_to_tuples(main_migrations) == [
    ('main_app', '0001_initial'),
    ('main_app', '0002_someitem_is_clean'),
    ('other_app', '0001_initial'),
    ('main_app', '0003_auto_20191119_2125'),
    ('main_app', '0004_auto_20191119_2125'),
    ('other_app', '0002_auto_20191120_2230'),
]

This way you can be sure that migrations and apps that depend on each other will be executed in the correct order.

Test framework integrations

We support several test frameworks as first-class citizens. That's a testing tool after all!

pytest

We ship django-test-migrations with a pytest plugin that provides two convinient fixtures:

  • migrator_factory that gives you an opportunity to create Migrator classes for any database
  • migrator instance for the 'default' database

That's how it can be used:

import pytest

@pytest.mark.django_db
def test_pytest_plugin_initial(migrator):
    """Ensures that the initial migration works."""
    old_state = migrator.before(('main_app', None))

    with pytest.raises(LookupError):
        # Models does not yet exist:
        old_state.apps.get_model('main_app', 'SomeItem')

    new_state = migrator.after(('main_app', '0001_initial'))
    # After the initial migration is done, we can use the model state:
    SomeItem = new_state.apps.get_model('main_app', 'SomeItem')
    assert SomeItem.objects.filter(string_field='').count() == 0

unittest

We also ship an integration with the built-in unittest framework.

Here's how it can be used:

from django_test_migrations.contrib.unittest_case import MigratorTestCase

class TestDirectMigration(MigratorTestCase):
    """This class is used to test direct migrations."""

    migrate_from = ('main_app', '0002_someitem_is_clean')
    migrate_to = ('main_app', '0003_auto_20191119_2125')

    def prepare(self):
        """Prepare some data before the migration."""
        SomeItem = self.old_state.apps.get_model('main_app', 'SomeItem')
        SomeItem.objects.create(string_field='a')
        SomeItem.objects.create(string_field='a b')

    def test_migration_main0003(self):
        """Run the test itself."""
        SomeItem = self.new_state.apps.get_model('main_app', 'SomeItem')

        assert SomeItem.objects.count() == 2
        assert SomeItem.objects.filter(is_clean=True).count() == 1

Credits

This project is based on work of other awesome people:

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-test-migrations-0.1.0.tar.gz (10.2 kB view details)

Uploaded Source

Built Distribution

django_test_migrations-0.1.0-py3-none-any.whl (9.4 kB view details)

Uploaded Python 3

File details

Details for the file django-test-migrations-0.1.0.tar.gz.

File metadata

  • Download URL: django-test-migrations-0.1.0.tar.gz
  • Upload date:
  • Size: 10.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/0.12.17 CPython/3.7.5 Darwin/18.6.0

File hashes

Hashes for django-test-migrations-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b8885dc22d4fedb177879af30225ecb48ddcbff0b31d5aa081d0ad5797951588
MD5 a3695f28df3c691e9de4f0b5670c30a4
BLAKE2b-256 414635bd85339ca6e97d583bf8059daf9cea70fdafdb5d649d6c2fa1d50f5a29

See more details on using hashes here.

File details

Details for the file django_test_migrations-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_test_migrations-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9d53a25b91c75f7385d84c2d804bcfe5b96bbf8219c280d7f436a859e55fbe2c
MD5 a164cb74e168625bbddfe9e57d193f15
BLAKE2b-256 778372ff82a8dd823ecc76c31c45999a2ba23260a8384f2bde166a32285dad4a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page