I’ve recently developed some Django sites where I was able to ensure
all features were thoroughly tested. I learnt a lot about writing
maintainable tests, and I’d like to share with you.
A basic view
When you’re starting out developing your site, your views will be
simple. Your index view probably looks something like this:
Now, what properties do we want this view to have? We want it to render without errors. Here’s a test:
Hey presto, your index view is tested!
When thinking about testing, you should ask yourself: ‘what properties do I want this view to have?’ In this case, we just want
to check the view renders. This helps us catch any silly errors (such
as incorrect template names) that may occur.
What’s nice about this test is that no legitimate refactoring will
cause the test to fail. Django provides many specific assertion
methods, such as assertTemplateUsed and
assertHTMLEqual. These have their uses, but they limit you. If
you use assertTemplateUsed and refactor your templates, you will
have to update your tests. Grrr.
Testing a form
Suppose we’re writing a form for users to sign up. Here’s what our
view looks like:
What are we interested in here? We want the page to load, we want
users to be able to register, and we want form validation. I’ve
written
django-test-mixins to
provide these assertions.
We don’t want tests to become a burden in later development. There are
two things we’ve done to ensure our tests are future-friendly.
Firstly, we’re careful to only make an assertion about the user we’re
interested in. We don’t want to assume how many users exist in the
system. Websites often start off with a single admin user, but we
shouldn’t bake that assumption into our tests. This assertion:
is worse, because we are making assumptions about the state of our
database.
Secondly, we’ve factored out the form parameters. The exact number of
form parameters often changes as features are added. It’s a pain to
change lots of tests because you’ve added one field to form, so keep
it DRY.
Testing protected views
Finally, let’s look at a more complex view.
We have authentication here and we need some existing models to test
the view.
We need a blog post to test this view, and we could explicitly create
one. This is tedious, and
milkman can do this for us.
Finally, we want an easy way of creating users to test that our view
applies authentication correctly.
We factor out user creation so we have a single place in our tests
that provides reusable methods for creating users according to th
eneeds of our site.
We want to test that our view validates users correctly and that it
modifies blog posts correctly. Since each test should only verify one
thing, we will write three tests.
This is how I write tests with Django. Don’t worry about writing
perfect tests, having tests at all is a huge boon. Once you have them,
don’t be afraid to keep iterating on them. Always seek out ways to
make testing easier next time.