Testing Utilities in Django
Lately I’ve had the opportunity to do some test-driven development with Django, which a) is awesome, I love testing, and b) means I’ve been working up a box full of testing utilities, and I figured I’d share them.
Convenient get()
and post()
methods
If you’ve done testing of views with Django you probably have some tests that look like:
def test_my_view(self):
response = self.client.get(reverse("my_url", kwargs={"pk": 1}))
response = self.client.post(reverse("my_url", kwargs={"pk": 1}), {
"key": "value",
})
This was a tad too verbose for my tastes so I wrote:
def get(self, url_name, *args, **kwargs):
return self.client.get(reverse(url_name, args=args, kwargs=kwargs))
def post(self, url_name, *args, **kwargs):
data = kwargs.pop("data", None)
return self.client.post(reverse(url_name, args=args, kwargs=kwargs), data)
Which are used:
def test_my_view(self):
response = self.get("my_url", pk=1)
response = self.post("my_url", pk=1, data={
"key": "value",
})
Much nicer.
login()
wrapper
The next big issue I had was logging in and out of multiple users was too verbose. I often want to switch between users, either to check different permissions or to test some inter-user workflow. That was solved with a simple context manager:
class login(object):
def __init__(self, testcase, user, password):
self.testcase = testcase
success = testcase.client.login(username=user, password=password)
self.testcase.assertTrue(
success,
"login with username=%r, password=%r failed" % (user, password)
)
def __enter__(self):
pass
def __exit__(self, *args):
self.testcase.client.logout()
def login(self, user, password):
return login(self, user, password)
This is used:
def test_my_view(self):
with self.login("username", "password"):
response = self.get("my_url", pk=1)
Again, a lot better.
django-fixture-generator
Not quite a testing utility, but my app django-fixture-generator has made testing a lot easier for me. Fixtures are useful in getting data to work wit, but maintaining them is often a pain, you’ve got random scripts to generate them, or you just checkin some JSON to your repository with no way to regenerate it sanely (say if you add a new field to your model). django-fixture-generator gives you a clean way to manage the code for generating fixtures.
In general I’ve found context managers are a pretty awesome tool for writing clean, readable, succinct tests. I’m sure I’ll have more utilities as I write more tests, hopefully someone finds these useful.