Skip to content

Guide: reproducible CI fixtures

seed() gives you realistic test data in one line. The key to using it in tests is the seed= argument — it makes the generated data deterministic, so a test behaves the same on your laptop and in CI, and a failure reproduces exactly.

pytest

import pytest
from django_data_seed import seed
from shop.models import Book

@pytest.mark.django_db
def test_book_list_shows_all_books(client):
    seed("shop.Book", count=20, seed=1)
    response = client.get("/books/")
    assert response.status_code == 200
    assert Book.objects.count() == 20

A neat pattern is to derive the seed from the test's name so every test gets its own stable dataset:

def _seed_for(request):
    return abs(hash(request.node.nodeid)) % (2**31)

@pytest.mark.django_db
def test_something(request):
    seed("shop.Order", count=50, seed=_seed_for(request))
    ...

Django TestCase

Seed once per test class in setUpTestData (fast — one insert, wrapped in a transaction that's rolled back per test):

from django.test import TestCase
from django_data_seed import seed

class BookViewTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        seed("shop.Book", count=20, seed=1)

    def test_list(self):
        self.assertEqual(self.client.get("/books/").status_code, 200)

Load-shaped data

Because the same call scales, "does this view survive a real table size?" becomes a five-line test instead of a staging exercise:

@pytest.mark.django_db
def test_book_list_at_scale(client, django_assert_max_num_queries):
    seed("shop.Book", count=100_000, seed=1, atomic="model")
    with django_assert_max_num_queries(5):
        client.get("/books/")

Tips

  • Pass --realism uniform / realism="uniform" if you want the fastest possible generation and don't need coherent, distribution-shaped data.
  • Keep skip_checks=False (the default) in CI so a fixture run also catches an unmigrated schema.