Organizing unit vs integration tests in Django

Hello,

I am building a Django REST API server. I have some tests in the app, but they are disorganized. My goal is to have a project structure with a clear testing structure like this:

project/
  app1/
     tests/
       unit/*
       api/*
       integration/*
  e2e_tests/...

Each app houses its own unit, REST api, and integration tests. There is a stand-alone directory with end-to-end tests.

To achieve this, I need to be able to classify tests into one of these 4 categories (unit, API, integration, and e2e). I am struggling with this.

I understand the idealized test pyramid like this (from book1 and book2):

  • unit tests:
    • very fast
    • do not require out-of-process dependencies
  • integration tests:
    • anything that’s not a unit test or an end-to-end
  • end-to-end tests:
    • extreme end of the integration testing spectrum
    • all out-of-process dependencies are mounted
    • HTML/JS is served, and a browser interacts with the app

It’s easy to draw the line between end-to-end and other tests, because a browser is involved.

However, how do I draw the line between unit, integration, and API tests?

  • Most of our “unit” tests use the ORM models. ORM models talk to the database. A database is an out-of-process dependency. Does that mean that any test using a model is an integration test?

  • Is a REST API test a type of integration test? What should an API test be testing? Should it test response codes, and shape of the returned data? Should it test the exact content of each response? If we are testing exact content, aren’t we duplicating “integration” tests?

I don’t have a clear mental picture. What are some good django-specific resources to learn about testing?

1 Like

distinguishing between units/integration/e2e API tests is seriously confusing, due to the over lap in what they often cover. here is how I usually think about it:

Units:

in the strictest sens, units should test individual components of the application in isolation. in Django context it means testing small parts of the code:

  • Models: units should mock DB interactions to avoid hitting actual DBs or you can rely on Django’s test db setup which might blur the lines into integration testing…

  • Views/Serializer: you can test these by mocking external dependencies and focusing on logic contained within the functions/class themself …

Api Test:

the focus on the interface and interaction from whatever you are exposing to the end user. A rest/soap/graphql/ws/potato/tomato whenever HTTP interface and ensure the backend behaves as expected when receiving specific requests…

  • Endpoint functionality/status codes / header / body
  • Data validation and assertions that output matches expected formats
  • Auth and permission here

Integration Tests:

tests between different components of your application, such as between models and 3rd party services. / database although using the ORM in tests makes them less “unit“ like, often practical to test the integration of your code with Django’s ORM.

End To End:

usually, tests simulate user interactions with the whole system from start to finish, they often run in an env that mimics the production env as closely as possible.

  • Browser interaction using something like selenium or Playwright
  • Full system testing everything including the front/back/database and any external services.

Resources to learn more:

django docs ofc: Testing tools | Django documentation | Django

and “Test-Driven Development with Python” by Harry Percival.

Thank you. The book, especially, will be valuable as a reference (already on its way from Amazon).