Django model fixtures are probably one of the most convenient way to deal with complex data dependencies while testing your application.
django fixtures are simply initial data in a serialized format such as json or yaml written to populate your models and explicit the relations between business entities. In the following example, some client is attached to division BU
- model: app.client pk: 1 fields: name: some client division_fk: 1
- model: app.division pk: 1 fields: name: division BU code_site: BU - model: app.division pk: 2 fields: name: some other division code_site: BA
What I often saw on Django projects and applied myself several times involves sourcing test data alongside your application somewhere in
your_app/ project/ settings.py .... app/ models.py tests.py .... test/ fixtures/ users.yaml clients.yaml retails.yaml divisions.yaml ....
and explicitly load the path in settings.py
FIXTURE_DIRS = ['./test/fixtures']
in order to easily populate your database before any test
class TestSomeApiRouting(TestCase): fixtures = ['users.yaml', 'clients.yaml', 'divisions.yaml'] def setUp(self): self.client = Client(HTTP_USER_AGENT='Mozilla/5.0') def test_clients_are_linked_to_divisions(self): response = self.client.get("/api/clients/") ....
This testing pattern is great, really straightforward. Moreover,
django.test.TestCase automagically loads fixtures in the right order based on data dependency. Unfortunately, unittest is sometimes a bit limited compared to pytest
I won't go into the details of how to migrate from unittest to pytest:
pytest-django documentation is pretty complete on the subject. The rest of the article presupposes that you've already made the migration and that you are able to run your django tests using
What I found most frustrating after this migration was that I lost this easy way to populate my database before tests. Using pytest involves to write every database fixture. In other words, the work I made while writing my yaml files was lost. Here is a trick I found to load models fixtures files using pytest as easily as you would do it with
pytestmark = pytest.mark.django_db @pytest.fixture(scope='session') def django_db_setup(django_db_setup, django_db_blocker): # no matter the order.... fixtures = ['users.yaml', 'clients.yaml', 'divisions.yaml'] with django_db_blocker.unblock(): for fixture in fixtures: with django_db_blocker.unblock(): # ...this will fix dependencies order try: call_command('loaddata', fixture) except IntegrityError: fixtures.append(fixture) @pytest.mark.django_db class TestSomeApiRouting def test_clients_are_linked_to_divisions(self): client = Client(HTTP_USER_AGENT='Mozilla/5.0') response = client.get("/api/clients/")