Skip to content

HTTP API Tests

Testing REST endpoints with Django-Ninja test client.

Basic Test Structure

import pytest
from ninja.testing import TestClient

from tests.integration.factories import TestClientFactory


@pytest.mark.django_db(transaction=True)
def test_health_endpoint(test_client_factory: TestClientFactory) -> None:
    client = test_client_factory()

    response = client.get("/v1/health")

    assert response.status_code == 200
    assert response.json() == {"status": "ok"}

Test Client

GET Requests

def test_get_users(test_client: TestClient) -> None:
    response = test_client.get("/v1/users/")
    assert response.status_code == 200

POST Requests

def test_create_user(test_client: TestClient) -> None:
    response = test_client.post(
        "/v1/users/",
        json={
            "email": "new@example.com",
            "username": "newuser",
            "first_name": "New",
            "last_name": "User",
            "password": "SecurePass123!",
        },
    )

    assert response.status_code == 200
    assert response.json()["username"] == "newuser"

PUT/PATCH Requests

def test_update_item(test_client: TestClient) -> None:
    response = test_client.put(
        "/v1/items/1",
        json={"name": "Updated Name"},
    )
    assert response.status_code == 200

DELETE Requests

def test_delete_item(test_client: TestClient) -> None:
    response = test_client.delete("/v1/items/1")
    assert response.status_code == 200

Authentication

Getting a Token

@pytest.fixture
def auth_token(
    test_client: TestClient,
    user_factory: TestUserFactory,
) -> str:
    user_factory(username="testuser", password="password123")

    response = test_client.post(
        "/v1/users/me/token",
        json={"username": "testuser", "password": "password123"},
    )

    return response.json()["access_token"]

Using the Token

def test_protected_endpoint(
    test_client: TestClient,
    auth_token: str,
) -> None:
    response = test_client.get(
        "/v1/users/me",
        headers={"Authorization": f"Bearer {auth_token}"},
    )

    assert response.status_code == 200

Authenticated Client Fixture

@pytest.fixture
def auth_client(
    test_client_factory: TestClientFactory,
    user_factory: TestUserFactory,
) -> TestClient:
    user = user_factory(username="authuser", password="password123")
    client = test_client_factory()

    # Get token
    response = client.post(
        "/v1/users/me/token",
        json={"username": "authuser", "password": "password123"},
    )
    token = response.json()["access_token"]

    # Return client with default auth header
    return test_client_factory(headers={"Authorization": f"Bearer {token}"})

Testing Error Responses

Validation Errors

def test_create_user_invalid_email(test_client: TestClient) -> None:
    response = test_client.post(
        "/v1/users/",
        json={
            "email": "not-an-email",
            "username": "user",
            "first_name": "Test",
            "last_name": "User",
            "password": "password123",
        },
    )

    assert response.status_code == 422
    assert "email" in str(response.json())

Authentication Errors

def test_protected_endpoint_no_token(test_client: TestClient) -> None:
    response = test_client.get("/v1/users/me")

    assert response.status_code == 401


def test_protected_endpoint_invalid_token(test_client: TestClient) -> None:
    response = test_client.get(
        "/v1/users/me",
        headers={"Authorization": "Bearer invalid-token"},
    )

    assert response.status_code == 401
    assert "Invalid token" in response.json()["detail"]

Not Found Errors

def test_get_nonexistent_item(test_client: TestClient) -> None:
    response = test_client.get("/v1/items/99999")

    assert response.status_code == 404

Database Markers

Transactional Tests

@pytest.mark.django_db(transaction=True)
def test_create_and_get_user(test_client: TestClient) -> None:
    # Create
    create_response = test_client.post("/v1/users/", json={...})
    user_id = create_response.json()["id"]

    # Get (same transaction)
    get_response = test_client.get(f"/v1/users/{user_id}")
    assert get_response.status_code == 200

Regular DB Access

@pytest.mark.django_db
def test_read_only(test_client: TestClient) -> None:
    # Read operations only
    response = test_client.get("/v1/health")
    assert response.status_code == 200

Complete Test Example

import pytest
from ninja.testing import TestClient

from tests.integration.factories import TestClientFactory, TestUserFactory


class TestUserEndpoints:
    @pytest.mark.django_db(transaction=True)
    def test_create_user(self, test_client: TestClient) -> None:
        response = test_client.post(
            "/v1/users/",
            json={
                "email": "test@example.com",
                "username": "testuser",
                "first_name": "Test",
                "last_name": "User",
                "password": "SecurePass123!",
            },
        )

        assert response.status_code == 200
        data = response.json()
        assert data["email"] == "test@example.com"
        assert data["username"] == "testuser"
        assert "password" not in data

    @pytest.mark.django_db(transaction=True)
    def test_create_user_duplicate_username(
        self,
        test_client: TestClient,
        user_factory: TestUserFactory,
    ) -> None:
        user_factory(username="existing")

        response = test_client.post(
            "/v1/users/",
            json={
                "email": "new@example.com",
                "username": "existing",
                "first_name": "Test",
                "last_name": "User",
                "password": "SecurePass123!",
            },
        )

        assert response.status_code == 400
        assert "Username already exists" in response.json()["detail"]

    @pytest.mark.django_db(transaction=True)
    def test_get_current_user(
        self,
        test_client: TestClient,
        user_factory: TestUserFactory,
    ) -> None:
        user = user_factory(username="me", password="password123")

        # Get token
        token_response = test_client.post(
            "/v1/users/me/token",
            json={"username": "me", "password": "password123"},
        )
        token = token_response.json()["access_token"]

        # Get user
        response = test_client.get(
            "/v1/users/me",
            headers={"Authorization": f"Bearer {token}"},
        )

        assert response.status_code == 200
        assert response.json()["username"] == "me"