Showing posts with label testing. Show all posts
Showing posts with label testing. Show all posts

Saturday, September 27, 2008

Mocking with Django and Google AppEngine

Since working with RSpec over the past 6 months - a Behaviour-Driven Development framework for ruby - I've been wondering if there's anything comparable in Python (my preferred tool for development!). One of the things I love about RSpec is the ease with which Mock objects can be used to keep tests focused.

While there are a number of mock libraries around for Python most don't result in particularly readable test code. But I was pleasantly suprised to discover Michael Foord's Mocking and Testing utilities.

A simple example: I've got a Django application hosted on Google AppEngine and say I want to write a simple test to verify that my view does require the user to be logged in with their Google account and if not, redirects appropriately - but I don't want to have to manually log a user in, or even use the Google api as part of my test. Here's a snippet showing how easy this is with Mock:

from mock import Mock
from google.appengine.api import users


class MySpecialView(TestCase):

def setUp():
""" Create the required mocks for the view tests """
# Mock the get_current_user method of the google users api
users.get_current_user = Mock()

# Create a mock user that we'll pretend is logged in
mock_user = Mock()

# Just for readability, save the special app-engine login url as
# an instance variable
self.url = reverse('my-special-view-name')
self.login_url = "http://testserver/_ah/login?continue=http%%3A//testserver%s" % self.url

def test_logged_in_user_can_access_page(self):
"""A logged in user should not be redirected to the login page"""
# Set the return value for the mocked
# get_current_user method:
users.get_current_user.return_value = mock_user

response = do_request()

# Make sure the mock method was called
self.assertTrue(users.get_current_user.called)

# And the redirect did not take place, but the
# normal template was rendered...
self.assertTemplateUsed(response, 'myapp/overview.html')

def test_anonymous_user_is_redirected_to_login(self):
""" An anonymous user should be redirected to the login page"""
# Set the google api's get_current_user method to return None
users.get_current_user.return_value = None

response = self.do_request()

# Make sure the mock method was called
self.assertTrue(users.get_current_user.called)

# And that the redirect took place... note we can't use
# the normal assertRedirects due to the app-engine specific
# login url.
self.assertEquals(response.status_code, 302)
self.assertEquals(
response['location'],
"http://testserver/_ah/login?continue=http%%3A//testserver%s" % self.url
)


Easy! Thanks Michael. The Mock object has lots of other goodies of course (such as auto-setting all the mock-methods from the real object, testing for call parameters etc.).