Django Testing Checklist

written by Chris Goodwin on 2020-08-10

You want to get started with Django tests, you've heard all the good things. You know that testing is a good thing and should be done because it will make your life a lot easier once you get it done. Testing your Django app will help you identify bugs, find bugs you didn't know where there, increase your code quality, find unused code, upgrade with confidence and beyond. But you probably have heard all of that before.

Sometimes people hear all the good things testing can do for them, but aren't exactly sure what to test. People newer to testing especially tend to suffer from a mindset of "well, it works locally - and I can see it works - so what should I be testing exactly?" Most people are at that point at sometime.

Testing in Django is pretty flexible. I personally prefer using pytest for my testing - but this checklist is not particular to any specific library you may be using and can be useful to anyone testing any Django application.

I think breaking up your tests into various test_ files helps to keep things a little more organized instead of the default tests.py file that Django gives you out of the box.

Since Django takes the approach of 'fat models', I think testing your model methods that get the data is some of the most important testing you can do! because if that data is wrong - your application is not going to do anyone any good.

Test the URLS (test_urls.py)

  • Test that your URL's point to the appropriate views
  • Test that the reverse method gives you back the appropriate URL you would expect
  • Test that a URL which is not a route gives appropriate response (404)

Test the models (test_models.py)

Test your model methods extensively - this is where a lot of your app is doing its work!

  • Test that model methods return correct data given the appropriate input
  • Test that model methods return correct response given bad input
  • Test that rights/permissions are working as expected if the methods are restricted in some way
  • Test for any edge cases that you can reasonably forsee

Test the views (test_views.py)

  • Test that your views render the appropriate template
  • Test that the views give the appropriate response
  • Test that a route without a view gives appropriate template (404)
  • Test the permissions on a view work as expected
    • No permissions? Should probably see a 403 error
    • Appropriate permissions? Make sure they see the correct template and get the correct response
  • Test what happens if an object doesn't exist in the view
    • Are your try/catch clauses working?
    • Are you seeing expected responses if something doesn't exist?
  • If you are using class based views
    • Test that a detail view gives 404 if the object doesn't exist
    • Test that a list view displays empty if no objects are there

Test the forms (test_forms.py)

  • Test that a form is valid if a user enters appropriate data
  • Test that a form is invalid if a user enters incorrect data
  • Test that your clean methods catch appropriate errors and return appropriate responses/messages

Test the app interactions (text_integrations.py)

I prefer to use selenium for interacting with a headless browser

  • Test that forms/logins work okay
  • Test that static files are loading without issue
  • Test that templates are displaying all the key peices you want (do not forget about your 400/403/404/500 templates)
  • Test for other behaviors a user might engage in to ensure your app works appropriately

There are obviously a few spots where you might want to mock up some dummy data (I prefer to use factory_boy for that). In most cases you can just create the models you need in the setUp method of the TestCase. For most average sized apps - you can just instanciate the model(s) you need in the setUp method and that is enough.

For larger apps with a lot of complexicity, I actually go through the process of creating a little file named data_setup.py where I create an 'apps worth' of dummy data complete with users of various permission levels and respective data. This helps tremendously with the integration testing for the most part, but can take a few extra seconds at the start of a test. To keep the data persistant through the duration of the TestCase instead of being torn-down/recreated for every test, I use the setUpTestData method.

Did you find this article helpful?

Feel free to help me keep this blog going (with coffee)!