utils.testhelpers — Testing utilities¶
Testing Django migrations¶
Warning
You can not test migrations if you have a MIGRATION_MODULES
setting that
disabled migrations. So make sure you remove that setting if you have it in your
test settings.
Guide¶
Lets say you have the following model:
class Node(models.Model):
name = models.CharField(max_length=255)
You have an initial migration, and you have created a migration named 0002_suffix_name_with_stuff
which looks like this:
suffix = ' STUFF'
def add_stuff_to_all_node_names(apps, schema_editor):
Node = apps.get_model('myapp', 'Node')
for node in Node.objects.all():
node.name = '{}{}'.format(node.name, suffix)
node.save()
def reverse_add_stuff_to_all_node_names(apps, schema_editor):
Node = apps.get_model('myapp', 'Node')
for node in Node.objects.all():
if node.name.endswith(suffix):
node.name = node.name[:-len(suffix)]
node.save()
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RunPython(add_stuff_to_all_node_names, reverse_code=reverse_add_stuff_to_all_node_names),
]
Note
You can not test migrations that can not be reversed, so you MUST write reversible migrations if you want to be able to test them. Think of this as a good thing - it forces you to write reversible migrations.
To test this, you can write a test case like this:
from ievv_opensource.utils.testhelpers import testmigrations
class TestSomeMigrations(testmigrations.MigrationTestCase):
app_label = 'myapp'
migrate_from = '0001_initial'
migrate_to = '0002_suffix_name_with_stuff'
def test_migrate_works(self):
# Add some data to the model using the ``apps_before`` model state
Node = self.apps_before.get_model('myapp', 'Node')
node1_id = Node.objects.create(
name='Node1'
).id
node2_id = Node.objects.create(
name='Node2'
).id
# Migrate (run the 0002_suffix_name_with_stuff migration)
self.migrate()
# Test using the ``apps_after`` model state.
Node = self.apps_after.get_model('myapp', 'Node')
self.assertEqual(Node.objects.get(id=node1_id).name, 'Node1 STUFF')
self.assertEqual(Node.objects.get(id=node2_id).name, 'Node2 STUFF')
def test_reverse_migrate_works(self):
# First, we migrate to get to a state where we can reverse the migration
self.migrate()
# Add some data to the model using the ``apps_after`` model state
Node = self.apps_after.get_model('myapp', 'Node')
node1_id = Node.objects.create(
name='Node1 STUFF'
).id
node2_id = Node.objects.create(
name='Node2 STUFF'
).id
# Reverse the migration
self.reverse_migrate()
# Test using the ``apps_before`` model state.
Node = self.apps_before.get_model('myapp', 'Node')
self.assertEqual(Node.objects.get(id=node1_id).name, 'Node1')
self.assertEqual(Node.objects.get(id=node2_id).name, 'Node2')
The MigrationTestCase class¶
-
class
ievv_opensource.utils.testhelpers.testmigrations.
MigrationTestCase
(methodName='runTest')[source]¶ Bases:
django.test.testcases.TransactionTestCase
Test case for a Django database migration.
Example:
class TestSomeMigrations(MigrationTestCase): migrate_from = '0002_previous_migration' migrate_to = '0003_migration_being_tested' def test_is_selected_is_flipped(self): MyModel = self.apps_before.get_model('myapp', 'MyModel') MyModel.objects.create( name='Test1', is_selected=True ) MyModel.objects.create( name='Test2', is_selected=False ) MyModel.objects.create( name='Test3', is_selected=True ) self.migrate() MyModel = self.apps_after.get_model('myapp', 'MyModel') self.assertEqual(MyModel.objects.filter(is_selected=True).count, 1) self.assertEqual(MyModel.objects.filter(is_selected=False).count, 2)
Create an instance of the class that will use the named test method when executed. Raises a ValueError if the instance does not have a method with the specified name.
-
app_label
= None¶ The django app_label for the app you are migrating. This is the same app_label as you use with
python manage.py makemigrations <app_label>
to create the migration.
-
migrate_dependencies
= None¶ Dependencies. A list of
(app_label, migration_name)
tuples.
-
migrate_from_dependencies
= None¶ Same as
migrate_dependencies
, but ONLY formigrate_from
.
-
migrate_to_dependencies
= None¶ Same as
migrate_dependencies
, but ONLY formigrate_from
.
-
migrate_from
= None¶ The name of the migration to migrate from. Can be the full name, or just the number (I.E.:
0002
or0002_something
.
-
migrate_to
= None¶ The name of the migration to migrate to. Can be the full name, or just the number (I.E.:
0003
or0003_something
.
-
classmethod
setUpClass
()[source]¶ Hook method for setting up class fixture before running tests in the class.
-
apps_before
¶ Get an
apps
object just like the first argument to a Django data migration at the state before migration has been run.Only available before
migrate()
has been called, or afterreverse_migrate()
has been called.
-
apps_after
¶ Get an
apps
object just like the first argument to a Django data migration at the state after migration has been run, and not available afterreverse_migrate()
has been called (unlessmigrate()
is called again).Only available after
migrate()
has been called.
-
migrate
()[source]¶ Migrate the database from
migrate_from
tomigrate_to
.
-
reverse_migrate
()[source]¶ Migrate the database from
migrate_to
tomigrate_from
.You must call
migrate()
before calling this.
-