Testing

Built-in testing tools

Django has really good testing tools:

Just like Django itself, we provide several built-in utilities for testing.

These includes subclasses of django.test.RequestFactory for sync and async requests. Use them for faster and simpler unit-tests:

We also have two subclasses of django.test.Client

What is the difference between the default ones? Not much:

  • Default Content-Type header is set to application/json

  • It is now easier to change Content-Type header as simple as specifying headers={'Content-Type': 'application/xml'} to change the content type for XML (or any other) requests and responses

  • Our test clients are faster, because they use msgspec if it is available to dump and parse json, instead of a regular json

Testing styles support

We support both:

For pytest we also have a bundled plugin with several different fixtures:

No need to configure anything, just use these fixtures by names in your tests.

You can use plain Django test primitives:

Or use dmr.test helpers when you want JSON defaults and controller-level testing with request factories:

Structured data generation

Since django-modern-rest is already built around an idea that we use models for everything, it is quite natural to reuse these models for tests as well.

For example, one can use Polyfactory to build test data from pydantic, msgspec, @dataclass, or even TypedDict models.

Let’s say you have this code for your controller, using pydantic models:

Let’s reuse the models for data generation in tests!

Which will make your tests simple, fast, and will help you find unexpected corner cases.

Property-based API testing

There’s a great tool called schemathesis that can be used to test your API to match your OpenAPI spec.

Official docs: https://schemathesis.readthedocs.io

schemathesis is not bundled together with the django-modern-rest. You have to install it with:

uv add --group dev schemathesis

Now, let’s see how you can generate thousands of tests for your API with just several lines of python code:

What will happen here?

  1. schemathesis loads OpenAPI schema definition from the reverse('openapi') URL

  2. Then we will create a top level schema object from the api_schema pytest fixture. It is needed to create a property-based test case

  3. Lastly, we create a generated test case with the help of @schema.parametrize()

You can also provide settings, like the number of generated tests, enabled rules, auth, etc:

When running the test case with

pytest tests/test_integration/test_openapi/test_schema.py

it will cover all your API. In simple cases it might be enough tests. Yes, you heard right: in simple cases just using schemathesis can remove the need to write any other integration tests.

Important

Using schemathesis with django-modern-rest is very easy, because we offer state-of-the-art OpenAPI schema generation. It will be really hard to satisfy schemathesis with a different framework.

Validating responses

schemathesis can also be used in regular tests to validate the response schema. See https://schemathesis.readthedocs.io/en/stable/guides/schema-conformance/

Example:

from dmr.test import DMRClient

def test_with_conditional_logic(dmr_client: DMRClient) -> None:
    response = dmr_client.post(
       '/users',
       data={'name': 'Alice'},
   )

   assert schema['/users']['POST'].is_valid_response(response.json())

API coverage with TraceCov

TraceCov can be used as an optional API coverage layer for django-modern-rest test suites. It complements regular integration tests and schemathesis runs by showing which OpenAPI operations and parameters were actually exercised.

Official docs: https://docs.tracecov.sh/

Note

TraceCov is not bundled with the django-modern-rest. You have to install it.

uv add --group dev tracecov

Why is this better than regular coverage?

Most coverage tools measure which lines and branches of your implementation were executed. TraceCov instead measures coverage of your API contract: which OpenAPI operations, parameters, and response variants were exercised.

This matters because “missing contract coverage” often turns into real bugs: edge cases for parameters, incorrect status codes, or response variants that your tests never actually reach. With django-modern-rest the OpenAPI schema is built from the project’s semantic schema (derived from response validation). That makes TraceCov coverage tightly aligned with what your implementation claims to support.

Configuration

How is that wired with django-modern-rest?

  • tracecov_map enables tracking for the whole test run.

  • When tracecov_map is active, any test that uses dmr_client or dmr_async_client is automatically included in the TraceCov report.

  • If you also run schemathesis, the schemathesis test records which validated requests and responses correspond to which OpenAPI operation, so the report can connect execution back to the spec.

To enable tracking, define a session-scoped tracecov_map fixture. When tracecov_map is configured and TraceCov is installed, dmr_client and dmr_async_client automatically register requests in tracecov.

To enable TraceCov recording for schemathesis runs, make sure your schemathesis test explicitly records validated interactions into tracecov_map via record_schemathesis_interactions(...).

What will happen here?

  1. schemathesis executes requests generated from your OpenAPI schema.

  2. After each schemathesis request is validated, the integration calls record_schemathesis_interactions(...) to record which OpenAPI operation and parameters were exercised for that verified response.

  3. Independently from schemathesis, any requests performed through dmr_client or dmr_async_client are tracked automatically when tracecov_map is active.

  4. TraceCov aggregates coverage across operations, parameters, keywords, and response coverage.

Tip

If TraceCov is not installed, or when tracecov_map is missing or inactive, fixtures return regular DMR clients without tracking.

When running your tests:

pytest tests/test_integration/test_openapi/test_schema.py

TraceCov generates a report in various formats. See TraceCov docs for details on the generated coverage report.

TraceCov view

In short: run schemathesis and regular integration tests together and get one unified TraceCov view of what your test suite actually exercised.