Python Programming, news on the Voidspace Python Projects and all things techie.

Additions and Updates to mock

emoticon:animals_cat I've made some additions to the mock module I posted. It now provides some helpers for doing class and module level monkey patching, plus some other minor improvements. The distribution now includes its own unit tests which are useful examples. Smile

I will maintain this module, but I don't intend to grow it a great deal. The point is that it isn't a mocking framework, but makes it easy to implement what are (in my experience) common testing patterns. I am using it with unittest.

Creating a Mock from a Spec

One of the points of unit testing is that you decouple tests of your units from other parts of your code, this is where you use mocks. The problem is that your tests then become coupled to your mocks instead of your production code.

If you provide a mock of a particular API, and you then refactor that API, your code is broken - but your tests will still pass because your mocks still provide the old API.

Mock now provides one solution to this, with the optional spec keyword in the constructor. This creates a mock, using another object (class or instance) as a spec. Only methods / attributes available on the specification object will be available on the mock. You can create a mock from a class without instantiating the class. If the class then changes, but your code or test uses the old API then the test will immediately fail with an AttributeError.

Creating Sentinel Objects

Sometimes when testing you need to test that a specific object is passed as an argument to another method, or returned. It can be common to create named sentinel objects to test this. sentinel provides a convenient way of creating and testing the identity of objects like this.

In this example we monkey patch method to return sentinel.ReturnValue. We want to test that this is the value returned when we call something.

from mock import Mock, sentinel

real = ProductionClass()

real.method = Mock()
real.method.return_value = sentinel.ReturnValue

returned = real.something()
self.assertEquals(returned, sentinel.ReturnValue, "something returned the wrong value")

Patch Decorators

A common need in tests is to patch a class attribute or a module attribute, for example patching a builtin or patching a class in a module to test that it is instantiated. Modules and classes are effectively global, so patching on them has to be undone after the test or the patch will persist into other tests and cause hard to diagnose problems.

The decorators patch and patch_module provide convenient ways of doing this.

mock = Mock()
@patch(SomeClass, 'class_method', mock)
def test():
    SomeClass.class_method()
test()

self.assertTrue(mock.called, "class_method not called")

The decorator is applied to a function (called test above). The patching only applies inside the body of the function. You have to call the function explicitly, this can be useful as the test function can take arguments and be used to implement several tests, it can also return values.

They can be stacked to perform multiple simultaneous patches:

mock1 = Mock()
mock2 = Mock()

@patch(SomeClass, 'class_method', mock1)
@patch(SomeClass, 'static_method', mock2)
def test():
    SomeClass.class_method()
    SomeClass.static_method()
test()

self.assertTrue(mock1.called, "class_method not called")
self.assertTrue(mock2.called, "static_method not called")

If you are patching a module (including __builtin__) then you can use the patch_module decorator, which takes a module name instead of an object to patch:

mock = Mock()
mock.return_value = sentinel.Handle

@patch_module('__builtin__', 'open', mock)
def test():
    return open('filename', 'r')

handle = test()

self.assertEquals(mock.call_args, (('filename', 'r'), {}), "open not called correctly")
self.assertEquals(handle, sentinel.Handle, "incorrect file handle returned")

The module name can be 'dotted', in the form package.module if needed.

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2007-11-22 00:48:14 | |

Categories: , ,


Mocking, Patching, Stubbing: all that Stuff

emoticon:pda I've been off home ill today, but I have wireless and a laptop...

I've been thinking about mocking, patching, stubbing and all that stuff.

We don't use a mocking library at Resolver, mainly because most of the libraries out there use the 'record -> replay' pattern for testing. The general consensus at Resolver is that 'action -> assertion' [1] is a more readable and intuitive pattern for testing.

As a result I've written an article on the subject, and a simple Mock class that implements the patterns we use most commonly through our test framework:

This is mainly me scratching my own itch, but I hope this will serve as a useful example for those wanting to roll their own mocks.

The one major thing it doesn't do is handle patching at a module / class level or patching out builtins (just another example of module level patching). Modules and classes are effectively globals - so any patching has to be undone which can make for annoying code to write.

I'm also not yet distributing the tests for mock.py - it currently uses some utilities from the Resolver test framework and I need permission to distribute these. Along with these are some decorators we use to simplify class / module patching which hopefully I will also be allowed to include.

Mock is really a very simple class (< 50 LOC), intended for use with unittest or similar test frameworks. This is an advantage, over-mocking is a sign that you have too many dependencies in your code. Smile

Examples:

>>> from mock import Mock
>>>
>>> real = ProductionClass()
>>> real.method = Mock()
>>>
>>> real.method(3, 4, 5, key='value')
>>>
>>> real.method.called
True
>>> real.method.call_args
((3, 4, 5), {'key': 'value'})
>>>
>>> mock = Mock()
>>> mock.something()
>>> mock.method_calls
[('something', (), {})]
>>>
>>> mock = Mock(methods=['something'])
>>> mock.something()
>>> mock.something_else()
Traceback (most recent call last):
   ...
AttributeError: object has no attribute 'something_else'

Setting the return values on a mock object is trivially easy:

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3

Of course you can do the same for methods on the mock:

>>> mock = Mock()
>>> mock.method.return_value = 3
>>> mock.method()
3

If you need an attribute setting on your mock, just do it:

>>> mock = Mock()
>>> mock.x = 3
>>> mock.x
3

Sometimes you want to mock up a more complex situation, like for example mock.connection.cursor().execute("SELECT 1"):

>>> mock = Mock()
>>> cursor = Mock()
>>> mock.connection.cursor.return_value = cursor
>>>
>>> mock.connection.cursor().execute("SELECT 1")
>>> mock.method_calls
[('connection.cursor', (), {})]
>>> cursor.method_calls
[('execute', ('SELECT 1',), {})]
>>>
[1]Well, 'setup -> action -> assertion' usually. I think this is pretty distinct from setting expectations, which merges the setup and the assertions before the action. Obviously this is a matter of opinion and taste [2]. Smile
[2]And to quote Guido on language design: it's largely a matter of taste, and some people have good taste... Wink

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2007-11-19 23:22:35 | |

Categories: , , ,


PyCon and Other Talks

emoticon:halt Well, the deadline for submitting talks for PyCon 2008 is really past now (or nearly anyway).

This year I've submitted two talks:

  • Python in your Browser with IronPython and Silverlight
  • Magic Python

I think that being able to script the browser with Python is exciting, but there is a twist in the first talk that should be of interest to all but the most Microsoft hardened cynics. Wink

The second is an introductory talk on the Python magic methods, which seemed to be one of the topics asked for. I'm not sure I'm up for doing two talks but we'll see if either of them get accepted.

My boss Giles has submitted his talk on End User Computing and Jonathan his talk on Test Driven Development. Both are very worthy talks, so it is all now down to the caprice of the reviewers. Smile

I've also been working on my presentation for Developer Day, this is a .NET community (yes there is such a thing) event in Reading on Saturday. The talk is 'Dynamic Languages on .NET' and it is based on the talk I gave at Mix UK earlier this year. At Mix I was asked to talk on IronPython and Silverlight, but the feedback I got was that the audience really wanted to know why they should be interested in dynamic languages at all. This talk will include a bit about Silverlight, but will be much more focused on what dynamic languages are and why they are exciting. It will also go over the .NET integration aspects of course.

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2007-11-19 23:21:40 | |

Categories: ,


Hosted by Webfaction

Counter...