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:
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?
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.