Expanding test coverage with ModelViewSetTestCase in Django Ninja CRUD

ModelViewSetTestCase

class ModelViewSetTestCase(django.test.TestCase)

A base class for testing ModelViewSet subclasses in Django.

Provides automated validation, binding, and test method registration for subclasses,
ensuring comprehensive and consistent testing of ModelViewSet instances.

Attributes:

  • model_viewset_class Type[ModelViewSet] - The ModelViewSet subclass to test.
  • base_path str - The base path for the model views.

Example:

  1. Define your ModelViewSet and register its routes:
# examples/views/department_views.py
from ninja import Router
from ninja_crud import views, viewsets

from examples.models import Department
from examples.schemas import DepartmentIn, DepartmentOut

router = Router()

class DepartmentViewSet(viewsets.ModelViewSet):
    model = Department
    default_input_schema = DepartmentIn
    default_output_schema = DepartmentOut

    list_view = views.ListModelView()
    create_view = views.CreateModelView()
    retrieve_view = views.RetrieveModelView()
    update_view = views.UpdateModelView()
    delete_view = views.DeleteModelView()

DepartmentViewSet.register_routes(router)
  1. Include your router in your Ninja API configuration:
# config/api.py
from ninja import NinjaAPI
from examples.views.department_views import router as department_router

api = NinjaAPI(urls_namespace="api")
api.add_router("departments", department_router)
  1. Create your test class by subclassing ModelViewSetTestCase:
# examples/tests/test_department_views.py
from ninja_crud import testing

from examples.models import Department
from examples.views.department_views import DepartmentViewSet

class TestDepartmentViewSet(testing.viewsets.ModelViewSetTestCase):
    model_viewset_class = DepartmentViewSet
    base_path = "api/departments"

    @classmethod
    def setUpTestData(cls):
        cls.department_1 = Department.objects.create(title="department-1")
        cls.department_2 = Department.objects.create(title="department-2")

    @property
    def path_parameters(self):
        return testing.components.PathParameters(
            ok={"id": self.department_1.id},
            not_found={"id": 9999}
        )

    @property
    def payloads(self):
        return testing.components.Payloads(
            ok={"title": "department-3"},
            bad_request={},
            conflict={"title": self.department_2.title},
        )

    test_list_view = testing.views.ListModelViewTest()
    test_create_view = testing.views.CreateModelViewTest(payloads)
    test_retrieve_view = testing.views.RetrieveModelViewTest(path_parameters)
    test_update_view = testing.views.UpdateModelViewTest(path_parameters, payloads)
    test_delete_view = testing.views.DeleteModelViewTest(path_parameters)

Notes:

This class should not be instantiated directly. Instead, it should be subclassed.
It utilizes the __init_subclass__ method to perform automatic validation,
binding of AbstractModelViewTest instances, and registration of test methods.

__init_subclass__

def __init_subclass__(cls, **kwargs)

Special method in Python that is automatically called when a class is subclassed.

For ModelViewSetTestCase subclasses, this method is used to validate the class
attributes, bind the AbstractModelViewTest instances to the subclass and to
their corresponding model views, and register the test methods. It should not be
called directly.