About me
● Professional Software
Engineer since June 2010
● Django user since
February 2018
● Software Engineering
Manager at
● SyDjango co-organiser
since August 2019
@tyomo4ka
XCore Project
XCore
myTrip.com
GoToGate
Trip.ru
XClaim
XCMS
XCover.com
Xtranet
BrightWrite
Partners
Dates
● Development started in Feb 2018
● Live since June 2019
Platform
● Python 3.6-3.7
● Django 2.0–2.2
● DRF 3.9-3.10
● 49,520 LOC
● 826 tests run in ~52 sec on Bitbucket
pipelines
● 86.9% code coverage
XCore Facts
v1.0
early days
Django
● Extends unittest
● Django helpers: client, settings, db fixtures, etc
● Extended and Django-specific assertions
Django
● django.test.SimpleTestCase – assertions, client, Django settings
● django.test.TransactionTestCase – reset db, fixtures, more
assertions
● django.test.TestCase – wraps each test in atomic block
● django.test.LiveServerTestCase – starts a real server
Django Rest Framework (DRF)
● Replicates Django’s test cases
● Replaces default client with APIClient
● RequestsAPIClient testing against an external server
● Unit tests are used to test the smallest bits of the application logic in
isolation from the rest of the application
● Integration testing is used to test larger parts of the application
which requires application communication with external services
(db, files, queues, etc)
Unit vs integration
Unit vs integration
● The same testing frameworks is usually used for both
● In most projects there is no way to run unit tests in separation from
integration tests
Unit
Integration
API
Cost
Number of tests on the project
Typical unit test scenario
1. Prepare SUT (System Under the Test)
○ could be skipped in many cases in others it’s as simple as manipulating with
objects in memory
○ achieve test isolation using mocking framework
○ no real IO calls (DB, queues, HTTP)
2. Run SUT logic
3. Perform assertions
○ typically using data returned back to the test
Typical integration test scenario
1. Prepare SUT
○ Reset DB, queues
○ Load fixtures – DB, files, configs, etc
○ Best practice is to avoid calling external (from the app perspective) systems – use
mock servers or mock Python IO calls
2. Run SUT logic
3. Perform assertions
○ typically checking the state of the system (DB, files, etc)
Unit vs integration in Django
● Always base your TestCase class of SimpleTestCase when writing
unit tests
● For integration and API tests use TestCase unless you really need to
test transaction.on_commit logic in which case you need to use
TransactionTestCase
● For UI tests use LiveServerTestCase
DB Fixtures (Django)
● Use dumpdata cli command to generate fixtures using data stored
in the DB
● Import in TestCases fixtures class property
DB Fixtures (factory_boy)
● Simple inheritance (inc. traits)
● Generated values: sequences, lazy attributes, faker, fuzzy attributes
● Native integration with Django models
● Non-db objects (JSON structures, POPO objects, etc)
v2.0
pre-production and early days in
production
pytest
● Run tests in parallel
● More flexibility around running tests
● Nicer output
pytest
● pytest --lf or pytest --ff
● pytest -k
● pytest --pdb
● markers, hooks, fixtures, etc
● plugins
assert magic
pytest-django
● --reuse-db
● set of useful django helpers
pytest-xdist
● run tests in parallel
● pytest -n auto or pytest -n ${NUM}
● pytest --looponfail
(run tests on remote servers via ssh)
Tests run > 50%
faster with
pytest-xdist
pytest-xdist
pytest-sugar
● instafail
● nice progressbar formatting
Parameterized tests (pytest)
Parameterized tests (ddt)
Parameterized tests (ddt)
HTTP requests mocking (responses)
pyvcr
pyvcr
freezegun
V3.0
scaling (future)
Plans for the future
● Small cleanups: replace responses with vcr everywhere, small test
refactoring
● Split test suite so we can run unit tests independently
● Smoke suite: migrate from Postman to pytest (or behave?)
● Add Hypothesis framework in some critical places
● Performance optimisation project: performance regression testing
Before we wrap up
Quality is everyone’s responsibility
Tests MUST run automatically in CI pipeline
End-to-end tests are important
Writing tests before code sometimes is helpful
Testing is the way not a way
But sometimes skipping testing is ok
What is not covered in this talk?
● Postman tests (monitors, smoke suite)
● Performance tests with Locust
● XFT building another Django app for testing insurance price
● UI testing with Selenium
● Behavioral testing with behave
Thanks for listening!
Feedback is welcome
Please reach me on Twitter @tyomo4ka

Testing Django APIs