Testing tools#

Django already provides a mature set of testing tools and advanced testing utilities. django-modern-rest builds on these primitives instead of replacing them.

It provides JSON-oriented request factories and test clients for both sync and async code, together with a bundled pytest plugin. All DMR testing tools use the same request API as their Django counterparts.

Choosing a tool#

Choose the narrowest tool that exercises the behavior you need:

Tool

Test level

Use it when

DMRRequestFactory

Sync unit test

You want to call a sync controller directly without URL routing or middleware.

DMRAsyncRequestFactory

Async unit test

You want the same focused test for an async controller.

DMRClient

Sync integration test

URL routing, middleware, and the complete Django request-response cycle are part of the behavior under test.

DMRAsyncClient

Async integration test

You need the complete request-response cycle with an async test client.

Request factories are usually the fastest choice for focused controller tests. Use a test client when routing, middleware, or another integration boundary matters.

Request factories#

DMRRequestFactory extends django.test.RequestFactory and creates WSGI requests. DMRAsyncRequestFactory extends django.test.AsyncRequestFactory and creates ASGI requests.

Like Django request factories, they create a request object but do not send it through URL routing or middleware. Pass the request directly to Controller.as_view() instead. For async controllers, create the request synchronously and await the controller response. Use DMRAsyncRequestFactory.wrap() when static type checkers need help understanding that the controller returns an awaitable.

The following tabs test the same controller with Django’s request factory, DMR’s request factory, and the DMR pytest fixture:

Test clients#

DMRClient extends django.test.Client, while DMRAsyncClient extends django.test.AsyncClient. Unlike request factories, test clients resolve the requested URL and execute the Django middleware chain.

This makes them a better fit for integration tests. The following tabs exercise the same endpoint with Django’s client, DMR’s client, and the DMR pytest fixture:

Async tools#

For async tests, use DMRAsyncRequestFactory or DMRAsyncClient. Their pytest equivalents are dmr_async_rf and dmr_async_client. Calls made with the async client and async controller responses must be awaited.

The DMR pytest plugin is registered automatically when pytest-django is installed; no conftest.py configuration is required. See the pytest plugin API reference for all available fixtures.

Sending data and checking responses#

JSON requests#

DMR request factories and clients use application/json as the default Content-Type for requests with a body. Dictionaries, lists, and other JSON-compatible values passed as data are serialized automatically. The DMR examples above rely on this default.

When msgspec is installed, DMR uses it to serialize request data and parse JSON responses. Otherwise, it falls back to Python’s json implementation. Strings and bytes are treated as already encoded and are not serialized again.

Custom content types#

Pass content_type when the request body uses a different representation, as in factory.post('/users/', data=body, content_type='application/xml'). You can also set the header explicitly with headers={'Content-Type': 'application/xml'}.

Checking responses#

Responses are regular Django django.http.HttpResponse instances. Check their status, headers, and decoded body using the standard Django API, as the test client example does.

response.json() requires the response Content-Type to be application/json. Inspect response.content directly for other formats.

Once you have the decoded value, choose an assertion that captures its complete stable contract without hard-coding runtime-generated data. The test assertions guide explains how to use inline-snapshot and dirty-equals for both cases.