wobbily_col
wobbily_col

Reputation: 11891

Disable data migrations in Django when creating test database

I have an app with a fair number of migrations including data migrations to set foreign keys on some models.

When I try to run tests.py, it fails because the data migration is querying the database for data that doesn't exist in the test database.

Is there a way to disable the data migrations? (I want to keep schema migrations, not disable migrations completely).

Or alternatively to load data from fixtures before running the data migrations?

Upvotes: 8

Views: 6991

Answers (5)

AR7
AR7

Reputation: 376

You can disable migrations from the settings.py file to speed up testing. Better create a separate settings files for the test and put the following code to disable the migrations:

MIGRATION_MODULES = {
'auth': None,
'contenttypes': None,
'default': None,
'sessions': None,

'core': None,
'profiles': None,
'snippets': None,
'scaffold_templates': None,
}

To run the tests with the custom settings file use the following command

python manage.py test --settings=project.tests_settings 

Upvotes: 1

Ivan
Ivan

Reputation: 81

I was inspired by Disable migrations while testing and @A. Grinenko. I did not customize TestRunner and I just checked whether 0002_data_migration.py was called by django test.
code:

class Migration(migrations.Migration):
dependencies = [
    ('backend', '0001_initial'),
]

operations = [
    migrations.RunPython(setup_data, rollback_setup, atomic=True)
] if 'test' not in sys.argv[1:] else []

Upvotes: 5

Vladimir
Vladimir

Reputation: 6726

For check either test are running or not:

'test' in sys.argv

Upvotes: 4

A. Grinenko
A. Grinenko

Reputation: 331

First of all I should notice that this solution is a dirty trick :)

I think that you should check whether your tests are running and exclude your data migrations from execution if the answer is True. To check whether tests are running you may define your own DiscoverRunner and set a variable showing that tests are running now.

A test runner is a class defining a run_tests() method. Django ships with a DiscoverRunner class that defines the default Django testing behavior. This class defines the run_tests() entry point, plus a selection of other methods that are used to by run_tests() to set up, execute and tear down the test suite.

For example, define TEST_RUNNER in your settings.

from django.conf import settings
from django.test.runner import DiscoverRunner

class MyTestSuiteRunner(DiscoverRunner):
    def __init__(self, *args, **kwargs):
        settings.TEST_RUN = True
        super().__init__(*args, **kwargs)

TEST_RUNNER = 'project_name.test_settings.MyTestSuiteRunner'

Let's assume then that your have only two migrations in your app:

  • 0001_initial.py
  • 0002_data_migration.py

So you should check in your data_migration whether tests are running and either include or not your operations.

from your_app.settings import settings
class Migration(migrations.Migration):

    dependencies = [
        ('your_app', '0001_initial.py'),
    ]

    operations = [
        migrations.RunPython(data_migration_method),
    ] if not settings.TEST_RUN else []

One more time I notice that in my opinion it's a dirty hack and it would be interesting to read how it should made in a proper way but this solution may help to solve your problem saving migration dependencies. Note that you can also add TEST_RUNNER in your test_settings run your tests as a AR7 have been suggested.

Also check the TEST_RUNNER documentation (https://docs.djangoproject.com/en/1.11/topics/testing/advanced/#defining-a-test-runner). I hope it will help you to find better solution.

Another solution is to use django-pytest instead of standard unittest module. Pytest provides a set of useful tools for testing Django applications and projects. It also helps to disable Django migrations and create the database by inspecting all models. Just use --nomigrations flag running your tests. Read documentation for more info (https://pytest-django.readthedocs.io/en/latest/database.html#nomigrations-disable-django-1-7-migrations)

Upvotes: 1

aliva
aliva

Reputation: 5720

you can run tests with --keepdb,

django docs:

You can prevent the test databases from being destroyed by using the test --keepdb option. This will preserve the test database between runs. If the database does not exist, it will first be created. Any migrations will also be applied in order to keep it up to date.

so you create a test database with valid data, and applied latest migrations, then run tests, and django won't try to create tables or run migrations while runnng tests :-)


note that instead of using keepdb I recommend that update your data migrations functions to be able to run them on empty dataset (for example in head of your migration function check if table is empty or not, if it's empty return with a warning message)

Upvotes: 2

Related Questions