Blog

The Digital Agency for International Development

New features in enhanced Django TestCase

By Chris Wilson on 09 January 2013

For about a year I've been working on our Django Binder app. It was originally designed for use in intranet applications, but much of the code is useful in Django apps of all kinds.

I've just committed a bunch of improvements from my work on the iSchool project, which I'd like to highlight to anyone interested, partly as internal documentation.

Module-level features

  • Removed the IntranetUser and IntranetGroup models, as they're not reusable, and the migrations, fixtures and management command. These models caused problems for people who added binder to their INSTALLED_APPS but didn't want to use the models, partly due to Django bug 19422.

Django database fields

You can import the binder.modelfields module in your models.py to access these field types.

  • Add a database field type for IP address ranges with a CIDR network mask, including validation.
  • Add a modified version of the ManyToManyField relation, which removes the annoying non-optional "help" text added by the Admin to all ManyToManyFields: Hold down "Control", or "Command" on a Mac, to select more than one. Even if the widget has been changed to checkboxes, where this instruction makes no sense.

Views

You can import the binder.views module in your views.py to access these mixins.

  • Add a LoginRequiredMixin for class-based views, since I needed it several times and overriding the dispatch method, just to add a decorator, started to annoy me.

Monkey patches

These can be applied by importing the binder.monkeypatches module, currently all-or-nothing.

  • Patch the Template.render method to show the name of the template file where the rendering failed with an exception. Now you actually know where to look for the error.
  • Patch URLNode.render to check for using a variable name that doesn't exist, which probably means that you forgot to quote the name of the view you wanted to link to with {% url %}. Otherwise it can be hard to work out where on the page the mistake is, without knowing what to look for.
  • Patch URLNode.render to check for using a variable name that points to a view that doesn't exist, and report the variable name and its value as well as the exception details, in case you used the wrong variable name in a {% url %}, or it had a value that you didn't expect.
  • Patch DateTimeField.get_prep_value to report the field name containing the naive datetime, until Django bug 19560 gets fixed.

Django TestCase improvements

These can be used by making your test cases extend AptivateEnhancedTestCase instead of Django's standard TestCase.

  • Fix the incorrect attempt to parse non-HTML responses from your views as HTML.
  • If the response is a SimpleTemplateResponse, ensure that it's rendered before returning it to your test case, so that you can access response.content safely.
  • If the response has no context but it does have a context_data (not sure why, but this happened to me, on an error response I think), then copy the context data so that context assertions don't crash.
  • Improve error messages if login as a test user fails, e.g. because the test password doesn't match the one in the fixtures.
  • Improve error message if non-dictionary passed to assertInDict. Otherwise it was easy to pass a list, and the error most confusing.
  • Split absolute_url helper into absolute_url_for_site and absolute_url_for_request. There are two ways of building an absolute URL in the app: looking at the domain of the Site record in the database, and looking at the one being accessed by the current user. Before, the way of doing this was ambiguous, but now it's clear. This breaks backwards compatibility with tests using the old, ambiguous way.
  • Allow passing a message to the extract_form helper, which gets the form out of a response.context, to be shown if the form is missing or the response has no context.
  • Report an error if update_form_values is called with values for fields that don't exist in the form. Just ignoring them leads to broken tests which are hard to debug.
  • Allow generating POST data with no value for a RadioSelect widget. When using this widget, it's possible for the user not to click on any of the radio buttons before submitting the form, i.e. not to make any selection, and we need to be able to test what happens in such cases.
  • Improve the sanity check in assert_followed_redirect for calling the test client's get or post methods without passing follow=True.
  • Add an assertion that the response does not contain a form with errors, and if it does, fail with the validation errors in the message. This simplifies tests for views redirect somewhere else if the form is valid, or show the form again if not, such as the CreateView and UpdateView class-based views.
  • Improve debugging of failed reverse() lookups. It's very unlikely that you passed in a callable when you meant a non-callable or vice versa, so we only show URLs matching that type, which shortens the list of possibilities quite significantly.