Test your django-piston API (with auth)

I have to build the API for one of my web service. It’s Django and django-piston powered application and it works well. Okay. I chose the TDD technique. So my problem was: how do I test the API parts which need authentication (basic HTTP, not OAuth for now)? The built-in test client of Django doesn’t seem to have such a feature.

So, here is my small workaround: you have to generate the HTTP_AUTHORIZATION field of your HTTP request. I wrote a small base test class for tests which need authentication:

import base64
import unittest
from django.test.client import Client

class BaseAuthenticatedClient(unittest.TestCase):
    def setUp(self):
        self.client = Client()
        auth = '%s:%s' % ('username', 'password')
        auth = 'Basic %s' % base64.encodestring(auth)
        auth = auth.strip()
        self.extra = {
            'HTTP_AUTHORIZATION': auth,
        }

You just have to replace username and password, and write your own test suite:

class TestAPIAuth(BaseAuthenticatedClient):

    def testrootauth(self):
        response = self.client.get('/api/aresource/id', {}, **self.extra)
        self.assertEqual(response.status_code, 200)

It should be OK. Have fun!

About Thomas Pelletier

17-year old student fond of math, science and IT. I like spending my spare time building websites with nice tools such as Django, Mercurial and Textmate. New technologies and web services are some of my passions. When I've something I'd like to talk about, I write a post and publish it on this website.
This entry was posted in None and tagged . Bookmark the permalink.

9 Responses to Test your django-piston API (with auth)

  1. Dave Morris says:

    You are absolutely right. You know what? It turns out that the password field in my fixture was messed up. Not sure how that happened, but it's working now. Thank you so much for your help.

  2. Dave Morris says:

    You are absolutely right. You know what? It turns out that the password field in my fixture was messed up. Not sure how that happened, but it's working now. Thank you so much for your help.

  3. I think not. Here is the client.get() definition:

    get(path, data={}, follow=False, **extra)

    If data is omitted (in yours), it has the same value as mine.

  4. Dave Morris says:

    the only difference I see is in the parameters at the end of the client.get() call:

    … {}, **self.extra)

    vs.

    … HTTP_AUTHORIZATION='Basic %s' % base64.encodestring('%s:%s' % ('dave','password')).rstrip())

    your has the extra {} in there. Does that make a difference?

  5. Just tested in my local application: both strip and rstrip work.

  6. This code does the same thing as mine, but it uses rstrip() insead of strip(). I'm not sure how it can impact on the result, yet try to change strip() to rstrip() in my code, and see if it works.

  7. Dave Morris says:

    I got some help from the django-piston google group, and it looks like this works:

    response = self.client.get('/api/stuff/', HTTP_AUTHORIZATION='Basic %s' % base64.encodestring('%s:%s' % ('dave','password')).rstrip())

    which doesn't match what your code does above. Any ideas why this way works for me and not your way?

  8. kizlum says:

    Yes, it looks like there is a problem in your configuration of your Piston auth backend (often set in urls.py). Paste your file here to see if we can fix it.

  9. Dave Morris says:

    When I try to use this method of http authentication in a unit test against a django-piston api, I receive the following error message:

    …/piston/authentication.py”, line 56, in is_authenticated
    request.user = self.auth_func(username=username, password=password)
    …/django/contrib/auth/init.py”, line 36, in authenticate
    user = backend.authenticate(**credentials)
    …/django/contrib/auth/backends.py”, line 19, in authenticate
    if user.check_password(password):
    …/django/contrib/auth/models.py”, line 197, in check_password
    return check_password(raw_password, self.password)
    …/django/contrib/auth/models.py”, line 53, in check_password
    algo, salt, hsh = enc_password.split('$')
    ValueError: need more than 2 values to unpack

    Could this be related to the realm setting in my piston urls.py file?

blog comments powered by Disqus