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’spytest.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-db
command 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:
Enable database access. Should be followed by a call torestore()
.
Disable database access. Should be followed by a call torestore()
.
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 db
fixture, 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 db
fixture. 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
: