When writing any kind of software, it’s crucial to test the code and therefore the application you’ve created. A small change somewhere can have a big impact somewhere else or there could be a bug that you’ve missed – either way, writing automated tests will help you achieve the following key objectives:
Robustness: Ensuring you catch edge cases in good time
Consistency: Ensuring that your code behaves the same way over time
Quality: Ensuring your application doesn’t break when changes are introduced
Django, a Python web framework, offers an inbuilt library for testing APIs. If you’ve worked with Python tests before, you’ll have come across the inbuilt ‘unittest’ library. Note that ‘TestCase’ is an extension of this Python library. In Django, this module can be imported into your test file as shown below:
After importing ‘TestCase’, each test class you write should inherit from this module. We’ve included an example below:
When you run your tests, Django’s own test utility checks for all test cases including subclasses of ‘TestCase’, as well as in all files that begin with the word ‘test’. Django then builds and runs a test suite for these test cases.
Note that the command for running tests in the terminal is:
The above command runs all the tests found in the project. However, you may wish to only run tests in a specific app, such as ‘employees’. You can achieve this by using the following command instead:
If you wish to test one test case only, use the following command:
Or test a single method:
When creating Django projects and apps, the framework comes with a boilerplate file and folder structure. In this default structure, there’s a file named ‘tests.py’ in each app folder.
So where should you place your tests in a Django project?
In our opinion, there isn’t a right or wrong answer and it mainly depends on the size of the project, including how it will scale and the size of the team working on it. Below, we’ve detailed 3 ways to organise your file/folder structure when working with Django tests:
In Option A, we’re working with the default boilerplate: the tests are in a file inside the app folder.
In Option B, we’ve created a separate folder for the tests, in which all the test cases are located.
In Option C, there’s a folder for tests and a subfolder for test cases from each app.
We’d posit that Option A works best for APIs with very few endpoints and apps, as well being applicable for apps that aren’t going to scale. Option B is likely to be best for mini applications that despite having several endpoints, are not meant to scale. Option C is definitely more scalable because of the organisation it offers as the project grows. You can even go ahead and put subfolders inside the first-level app subfolder for unit tests, integration tests and more.
Please note that we're only testing the custom apps that we’ve built in our project. We don’t recommend testing either Django’s or Python’s inbuilt modules/files in the project. These include ‘settings.py’, ‘urls.py’ and more.
What should be tested?
● Custom views and extended view logic
● Models and their methods
● Custom validators and helpers
In instances where you’ve extended Django’s ‘BaseUser’ class, make sure to only test the additional fields you provide in the model.
Below, we’ve demonstrated how to write a simple test case in Django:
Start by defining the method ‘setUp’, which highlights the things you’ll need to be completed before each test method runs. Three things to note:
- Django automatically runs ‘tearDown’
- ‘setUp’ and ‘tearDown’ should be ran at the beginning and end of each test method
- Django creates a test database where all data generated during the tests is stored. This database is dropped once the tests have finished running
Django has an inbuilt client that you can use to make API requests when testing. In the example above, we created an instance of this client in line 7 and used it to communicate with the API in line 22.
Whilst writing test code isn’t the most fun challenge, it’s a necessary ‘evil’ to help ensure that your code is safe, reliable and can’t be broken when new changes are introduced. Interested in learning more? Make sure to check out our other articles on all-things Django.