Wednesday, May 20, 2009

Outside-in development on Ubuntu

For over 6 months now I've been working on - collaborative development for open source software - using Python (Zope). It's been an unreal experience seeing various testing strategies on such a large code-base, but the two things I miss the most while developing on launchpad are:
  1. The ability to do outside-in development properly - as is possible with ruby's Cucumber project (although, this could be used, but it'd be adding yet-another-technology to the stack - I haven't tried pyCucumber, but it doesn't look too active), and
  2. Something similar to rspactor - allowing tests to be run automatically as I edit files, and reporting them via the OS's notification system.
But trying to communicate exactly what I mean by that to people who haven't used rspactor or Cucumber is kind-of tricky, so here's a video demo'ing how I'd love to do inside-out development on Ubuntu...

Tuesday, October 14, 2008

Mocking in Python just got easier

Voidspace just released Mock 0.4.0 - an update to the excellent Mock library which brings a few conveniences and easier patching of module and class-level attributes.

This makes tests using mocks easier to read, for example inside a test case:

(('goodbye',),{'hello': False}))

Can now be written as:

my_mock.my_method.assert_called_with('goodbye', hello=False)

Check it out!

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

# 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

# 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)
"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.).