Testing django admin customisations

In preparation for an upgrade, I've been writing unit tests for a Django app with the help of a fantastic book -- Django 1.1 Testing and Debugging by Karen M. Tracey, there'll be a review coming up when I finish it.

I had some issues with the code to test admin customisations (Chapter 4), I want to document the changes I made for future reference.

Error 403

Despite using client.login() in the setUp() method, response returned with a status_code of 403 (Forbidden) when creating a new item.

class AdminCustomisationTest(TestCase):

    def setUp(self):
        username = 'test_user'
        pwd = 'secret'

        self.u = User.objects.create_user(username, '', pwd)
        self.u.is_staff = True
        self.u.is_superuser = True
        self.u.save()

        self.assertTrue(self.client.login(username=username, password=pwd),
            "Logging in user %s, pwd %s failed." % (username, pwd))

        Survey.objects.all().delete()

    def tearDown(self):
        self.client.logout()
        self.u.delete()

    def test_add_survey_ok(self):
        self.assertEquals(Survey.objects.count(), 0)

        post_data = { 'title': u'Test OK',
                      'open_date': datetime.now(),
                    }

        response = self.client.post(reverse('admin:survey_survey_add'), post_data)

        self.assertRedirects(response, reverse('admin:survey_survey_changelist'))
        self.assertEquals(Survey.objects.count(), 1)

At first I blamed some customisations I did to the authentication backends to allow OpenID, however printing the response.content revealed otherwise:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>

The Django CSRF protection system prevented the tests from passing. Note that this app runs on Django 1.1, Django 1.2 overhauled the CSRF system and would likely work without problems.

I'm sure there are more elegant ways to solve this. Here's a method that works:

def get_csrf_token(self, response):
    csrf = "name='csrfmiddlewaretoken' value='"
    start = response.content.find(csrf) + len(csrf)
    end = response.content.find("'", start)

    return response.content[start:end]

def test_add_survey_ok(self):
    self.assertEquals(Survey.objects.count(), 0)

    response = self.client.get(reverse('admin:survey_survey_add'))
    csrf = self.get_csrf_token(response)
    post_data = { 'title': u'Test OK',
                  'open_date': datetime.now(),
                  'csrfmiddlewaretoken': csrf,
                }

    response = self.client.post(reverse('admin:survey_survey_add'), post_data)

    self.assertRedirects(response, reverse('admin:survey_survey_changelist'))
    self.assertEquals(Survey.objects.count(), 1)

(Note/Update: If I had waited until the next chapter, I would have found out how to integrate Twill with Django apps and none of this would have been necessary -- haha!)

This field is required (datetime)

After this, although the response.status_code was finally 200, the article still wasn't created. Peering through response.content showed that the datetime field was considered to be missing. This is what the postdata should look like instead for a datetime field:

post_data = { 'title': u'Test OK',
              'open_date_0': '2011-03-17',
              'open_date_1': '9:50:00',
              'csrfmiddlewaretoken': csrf,
            }

Test passes!

links

social