Run tests using docker-compose run --rm test
The above command executes tests defined in the tests.py
files and compares
the API responses with the snapshots saved in the snapshots/snap_tests.py
files.
To update the snapshots of the API responses, execute
docker-compose run --rm test_snapshot_update
The above command updates the snapshots/snap_tests.py
files with the API
responses of the tests defined in the tests.py
files. Do NOT manually edit
the contents of the snapshots/
folders.
Write tests in the tests.py
file under each django app.
First, create a factory for each model in the app. A model's factory can be used to create multiple instances of the model.
For example, we define a simple model,
class ExampleModel(models.Model):
name = models.TextField(verbose_name=_('name'))
age = models.IntegerField(verbose_name=_('age'))
To test API endpoints which depend on ExampleModel
, we first create a factory
using a handy library called
factory_boy. ExampleFactory
is
defined as,
class ExampleFactory(factory.django.DjangoModelFactory):
name = 'Example Instance'
age = 42
A good factory covers all possible values accepted by the model. To achieve
sufficient randomness, we use
fuzzy attributes that
generate random values for each instance of ExampleModel
created by
ExampleFactory
.
class ExampleFactory(factory.django.DjangoModelFactory):
name = fuzzy.FuzzyText(length=10, prefix='example-')
age = fuzzy.FuzzyInteger(0)
Now we are ready to write test cases for API endpoints involving ExampleModel
.
A real-world factory is
ProjectFactory
which is used to
create instances of the Project
model.
For list
test cases we make a GET
request,
class TestExampleAPI(TestCase):
def test_example_list(self):
# submit list request
response = self.client.get("/api/v2/example/")
# check response
self.assertEqual(response.status_code, 200)
self.assertMatchSnapshot(json.loads(response.content))
The test_example_list
test case calls the GET /api/v2/example/
endpoint. It
checks for the response status code using assetEqual
and response body using
assertMatchSnapshot
and json.loads
(only if the expected response is in JSON
format).
By default, the test run starts with an empty database. So test_example_list
will always return an empty list.
To check for a response with a non-empty list response, first populate the
database with an instance of ExampleModel
. Call the
create
function on ExampleFactory
to create an instance of ExampleModel
,
def test_example_list_one(self):
# create instance
ExampleFactory.create()
# submit list request
response = self.client.get("/api/v2/example/")
# check response
self.assertEqual(response.status_code, 200)
self.assertMatchSnapshot(json.loads(response.content))
To create multiple instances, call the
create_batch
function.
We create an instance of ExampleModel
to submit to the POST
endpoint,
create
endpoints require user authentication. Authentication is possible via
the
force_login
function.
def test_example_create(self):
# create instance
new_name = "Mock Example for Create API Test"
new_example = ExampleFactory.stub(name=new_name)
# authenticate
new_user = UserFactory.create()
self.client.force_login(new_user)
# submit create request
response = self.client.post(
"/api/v2/example/", new_example, content_type="application/json"
)
# check response
self.assertEqual(response.status_code, 201)
self.assertMatchSnapshot(json.loads(response.content))
self.assertTrue(ExampleModel.objects.get(name=new_name))
The test_example_create
test case calls the POST /api/v2/example/
endpoint
with a request body. It also uses assertTrue
to check if the ExampleModel
object is created as a result of the POST request.
Using methods used in the above examples, test cases can be written for
update
, read
and delete
endpoints.
Although our factories generate random attributes, we require the API response to be exactly the same at each run to have comparable snapshots.
Reproducibility is achieved by setting a random seed, using mocks and resetting the database before executing each test.
An example of a real-world snapshot test is defined for
TestProjectAPI
.
Run docker-compose run --rm coverage_report
to view a report of the lines of
code executed to run the tests. Report includes coverage of both the
snapshottests and drf tests.
For a line-by-line coverage report run docker-compose run --rm coverage_html
and view htmlcov/index.html
in a browser.