Django Custom Test Runner

The default database construction mostly follows Django’s own test runner. You can however influence all parts of the database setup process to make it fit in projects with special requirements. This section assumes some familiarity with the Django test runner, Django database creation and pytest fixtures. The second most significant change is that the django.test.utils functions setuptestenvironment and teardownenvironment calls are executed by connecting to the signal rather than being called directly from the methods of the test runner. This serves as a way to test that the signals are being fired, as Django tests will fail if those. These Validation are run when you are trying to create an instance of a model. Technically, this validation is implemented after you run ModelName.objects.create(data = data). Syntax – fieldname = models.Field(validators = function 1, function 2) Django Custom Field Validation Explanation. Illustration of validators using an Example. If this checkbox is selected, Django test will run with the specified custom settings, rather than with the default ones. Specify the fully qualified name of the file that contains Django settings. You can either type it manually, in the text field to the right, or click the browse button, and select one in the dialog that opens.

pytest-django takes a conservative approach to enabling databaseaccess. By default your tests will fail if they try to access thedatabase. Only if you explicitly request database access will this beallowed. This encourages you to keep database-needing tests to aminimum which makes it very clear what code uses the database.

Enabling database access in tests

You can use pytest marks to tell pytest-django yourtest needs database access:

It is also possible to mark all tests in a class or module at once.This demonstrates all the ways of marking, even though they overlap.Just one of these marks would have been sufficient. See the pytestdocumentation for detail:

By default pytest-django will set up the Django databases thefirst time a test needs them. Once setup, the database is cached to beused for all subsequent tests and rolls back transactions, to isolatetests from each other. This is the same way the standard DjangoTestCase uses the database. Howeverpytest-django also caters for transaction test cases and allowsyou to keep the test databases configured across different test runs.

Testing transactions

Django itself has the TransactionTestCase whichallows you to test transactions and will flush the database betweentests to isolate them. The downside of this is that these tests aremuch slower to set up due to the required flushing of the database.pytest-django also supports this style of tests, which you canselect using an argument to the django_db mark:

Tests requiring multiple databases

New in version 4.3.

Caution

This support is experimental and is subject to change withoutdeprecation. We are still figuring out the best way to expose thisfunctionality. If you are using this successfully or unsuccessfully,let us know!

pytest-django has experimental support for multi-database configurations.Currently pytest-django does not specifically support Django’smulti-database support, using the databases argument to thedjango_db mark:

For details see django.test.TransactionTestCase.databases anddjango.test.TestCase.databases.

--reuse-db - reuse the testing database between test runs

Using --reuse-db will create the test database in the same way asmanage.pytest usually does.

However, after the test run, the test database will not be removed.

The next time a test run is started with --reuse-db, the database willinstantly be re used. This will allow much faster startup time for tests.

This can be especially useful when running a few tests, when there are a lotof database tables to set up.

--reuse-db will not pick up schema changes between test runs. You must runthe tests with --reuse-db--create-db to re-create the database accordingto the new schema. Running without --reuse-db is also possible, since thedatabase will automatically be re-created.

--create-db - force re creation of the test database

When used with --reuse-db, this option will re-create the database,regardless of whether it exists or not.

Example work flow with --reuse-db and --create-db.

A good way to use --reuse-db and --create-db can be:

  • Put --reuse-db in your default options (in your project’s pytest.ini file):

  • Just run tests with pytest, on the first run the test database will becreated. The next test run it will be reused.

  • When you alter your database schema, run pytest--create-db, to forcere-creation of the test database.

--no-migrations - Disable Django migrations

Using --no-migrations (alias: --nomigrations) will disable Django migrations and create the databaseby inspecting all models. It may be faster when there are several migrations torun in the database setup. You can use --migrations to force runningmigrations in case --no-migrations is used, e.g. in setup.cfg.

Advanced database configuration

pytest-django provides options to customize the way database is configured. Thedefault database construction mostly follows Django’s own test runner. You canhowever influence all parts of the database setup process to make it fit inprojects with special requirements.

This section assumes some familiarity with the Django test runner, Djangodatabase creation and pytest fixtures.

Fixtures

There are some fixtures which will let you change the way the database isconfigured in your own project. These fixtures can be overridden in your ownproject by specifying a fixture with the same name and scope in conftest.py.

Use the pytest-django source code

The default implementation of these fixtures can be found infixtures.py.

The code is relatively short and straightforward and can provide astarting point when you need to customize database setup in your ownproject.

django_db_setup

This is the top-level fixture that ensures that the test databases are createdand available. This fixture is session scoped (it will be run once per testsession) and is responsible for making sure the test database is available for teststhat need it.

The default implementation creates the test database by applying migrations and removesdatabases after the test run.

You can override this fixture in your own conftest.py to customize how testdatabases are constructed.

django_db_modify_db_settings

This fixture allows modifyingdjango.conf.settings.DATABASESjust before the databases are configured.

If you need to customize the location of your test database, this is thefixture you want to override.

The default implementation of this fixture requests thedjango_db_modify_db_settings_parallel_suffix to provide compatibilitywith pytest-xdist.

This fixture is by default requested from django_db_setup.

django_db_modify_db_settings_parallel_suffix

Requesting this fixture will add a suffix to the database name when the testsare run via pytest-xdist, or via tox in parallel mode.

This fixture is by default requested fromdjango_db_modify_db_settings.

django_db_modify_db_settings_tox_suffix

Requesting this fixture will add a suffix to the database name when the testsare run via tox in parallel mode.

This fixture is by default requested fromdjango_db_modify_db_settings_parallel_suffix.

django_db_modify_db_settings_xdist_suffix

Requesting this fixture will add a suffix to the database name when the testsare run via pytest-xdist.

This fixture is by default requested fromdjango_db_modify_db_settings_parallel_suffix.

django_db_use_migrations

Returns whether or not to use migrations to create the testdatabases.

The default implementation returns the value of the--migrations/--no-migrations command line options.

This fixture is by default requested from django_db_setup.

django_db_keepdb

Returns whether or not to re-use an existing database and to keep it after thetest run.

The default implementation handles the --reuse-db and --create-dbcommand line options.

This fixture is by default requested from django_db_setup.

django_db_createdb

Returns whether or not the database is to be re-created before running anytests.

This fixture is by default requested from django_db_setup.

django_db_blocker

Warning

It does not manage transactions and changes made to the database will notbe automatically restored. Using the pytest.mark.django_db markeror db fixture, which wraps database changes in a transaction andrestores the state is generally the thing you want in tests. This markercan be used when you are trying to influence the way the database isconfigured.

Database access is by default not allowed. django_db_blocker is the objectwhich can allow specific code paths to have access to the database. Thisfixture is used internally to implement the db fixture.

django_db_blocker can be used as a context manager to enable databaseaccess for the specified block:

You can also manage the access manually via these methods:

Django Custom Test Runner
django_db_blocker.unblock()

Enable database access. Should be followed by a call torestore().

django_db_blocker.block()

Disable database access. Should be followed by a call torestore().

django_db_blocker.restore()

Restore the previous state of the database blocking.

Examples

Using a template database for tests

This example shows how a pre-created PostgreSQL source database can be copiedand used for tests.

Put this into conftest.py:

Using an existing, external database for tests

This example shows how you can connect to an existing database and use it foryour tests. This example is trivial, you just need to disable all ofpytest-django and Django’s test database creation and point to the existingdatabase. This is achieved by simply implementing a no-opdjango_db_setup fixture.

Put this into conftest.py:

Populate the database with initial test data

In some cases you want to populate the test database before you start thetests. Because of different ways you may use the test database, there aredifferent ways to populate it.

Populate the test database if you don’t use transactional or live_server

If you are using the pytest.mark.django_db() marker or dbfixture, you probably don’t want to explicitly handle transactions in yourtests. In this case, it is sufficient to populate your database onlyonce. You can put code like this in conftest.py:

This loads the Django fixture my_fixture.json once for the entire testsession. This data will be available to tests marked with thepytest.mark.django_db() mark, or tests which use the dbfixture. The test data will be saved in the database and will not be reset.This example uses Django’s fixture loading mechanism, but it can be replacedwith any way of loading data into the database.

Notice django_db_setup in the argument list. This triggers theoriginal pytest-django fixture to create the test database, so that whencall_command is invoked, the test database is already prepared andconfigured.

Populate the test database if you use transactional or live_server

In case you use transactional tests (you use the pytest.mark.django_db()marker with transaction=True, or the transactional_db fixture),you need to repopulate your database every time a test starts, because thedatabase is cleared between tests.

The live_server fixture uses transactional_db, so youalso need to populate the test database this way when using it.

You can put this code into conftest.py. Note that while it it is similar tothe previous one, the scope is changed from session to function:

Use the same database for all xdist processes

By default, each xdist process gets its own database to run tests on. This isneeded to have transactional tests that do not interfere with each other.

If you instead want your tests to use the same database, override thedjango_db_modify_db_settings to not do anything. Put this inconftest.py:

Randomize database sequences

You can customize the test database after it has been created by extending thedjango_db_setup fixture. This example shows how to give a PostgreSQLsequence a random starting value. This can be used to detect and preventprimary key id’s from being hard-coded in tests.

Put this in conftest.py:

Django Custom Test Runners

Create the test database from a custom SQL script

You can replace the django_db_setup fixture and run any code in itsplace. This includes creating your database by hand by running a SQL scriptdirectly. This example shows sqlite3’s executescript method. In a moregeneral use case, you probably want to load the SQL statements from a file orinvoke the psql or the mysql command line tool.

Put this in conftest.py:

Warning

This snippet shows cursor().executescript() which is sqlite specific, forother database engines this method might differ. For instance, psycopg2 usescursor().execute().

Use a read only database

You can replace the ordinary django_db_setup to completely avoid databasecreation/migrations. If you have no need for rollbacks or truncating tables,you can simply avoid blocking the database and use it directly. When using thismethod you must ensure that your tests do not change the database state.

Django Test Client

Put this in conftest.py: