Django REST API Testing Primer

In this primer we will create a simple API test for Django API using pytest as testing framework.

Define pytest mark

I like grouping tests using @pytest.mark. Our test covers full scenario so I am marking it as end-to-end test. Before this I need to define an end2end marker in pytest.ini:

[pytest]
junit_family=xunit1
DJANGO_SETTINGS_MODULE=todos.test_settings
markers=
   end2end: mark a test as end-to-end test
addopts =
   -v
   -m not end2end

Test Implementation

import pytest
from django.test import Client
from django.urls import reverse
from rest_framework import status

class TestTodo

   def test_should_fail_to_list_when_not_authenticted(self):
      #
      # Given
      request.getfixturevalue('given_truncated_user_model')
      #
      # When request list of todos, matching a query
      url = reverse("todo-list")
      response = client.get(url, query)
      # Then request fails because not authorized
      assert response.status_code == status.HTTP_401_UNAUTHORIZED

   def test_should_return_list_of_todos_matching_query(self, request, client: Client):
      #
      # Given
      request.getfixturevalue('given_truncated_user_model')
      # AND
      request.getfixturevalue('given_logged_in_user')
      # AND
      request.getfixturevalue('given_truncated_todos')
      # AND
      search_query = request.getfixturevalue('given_search_query')
      # AND
      todos_that_match = request.getfixturevalue('given_todos_that_match_search_query')
      # AND
      request.getfixturevalue('todos_that_do_not_match_search_query')
      #
      # When request list of todos, matching a query
      url = reverse("todo-list")
      response = client.get(url, search_query)
      #
      # Then request succeeds with status 200-OK
      assert response.status_code == status.HTTP_200_OK
      # AND
      response_data = response.json()
      assert response_data["count"] == len(todos_that_match)

   @pytest.fixture
   def given_truncated_user_model(self, django_user_model):
      django_user_model.objects.all().delete()
      return django_user_model

   @pytest.fixture
   def given_existing_user(self, django_user_model):
      user = django_user_model.objects.create(
            username="todouser", is_staff=True
      )
      return user

   @pytest.fixture
   def given_logged_in_user(self, request, client: Client):
      user = request.getfixturevalue("given_existing_user")
      client.force_login(user)
      return user

   @pytest.fixture
   def given_truncated_todos(self):
      # TODO: Implement

   @pytest.fixture
   def given_search_query(self):
      return {"search": "pilates"}

   @pytest.fixture
   def given_todos_that_match_search_query(self):
      # TODO: Add todos that match the query
      # TODO: Return the todos

   @pytest.fixture
   def todos_that_do_not_match_search_query(self):
      # TODO: Add todos that do not match the query
      # TODO: Return the todos