What and Why

PyMock is for developers who want to test their code at the unit level. It allows you to imitate the behavior of methods in limited situations without relying on their implementation. This can be done by hand, but creating mocking classes from scratch takes effort and time. PyMock makes this process easier.

Downloading and Installing

PyMock is available as a python source distribution. Download the file pymock-1.0.5.1-py2.5.egg. Put it into your python site directory, and then require it as needed. PyMock is also availabe through easy_install. If you've already installed setuptools the just run easy_install PyMock.

Previous releases: pymock-1.0.5.tar.gz, pymock-1.0.4.tar.gz, pymock-1.0.tar.gz

What is New in 1.0.5.1

What is New in 1.0.5

What is New in 1.0.4

This release is a bug fix. There were a number of egregious bugs relating to the unittest.TestCase that have been fixed. The behavior now matches that documented in the examples.

More Details

PyMock is based on the java easymock package http://www.easymock.org. It uses a recording and replay model rather than using a specification language. Easymock lives up to it's name compared to other mocking packages. PyMock takes advantage of python's dynamic nature for futher improvements.

In addition to allowing you to mock out simple function calls PyMock allows you to mock out properties, generators, and raw functions. It can also temporarily override all of these entities within existing classes and objects. (For instance you can mock out os.listdir() for a single test.

PyMock does have several weakeness. You can't specify dynamic parameter value checks, nor can you verify ordering between calls.

Be warned of several things. PyMock may not work if you're doing deep black magic with metaclasses. You're likely to run into its machinery. If you check object types explicitly you're likely to it's machinery too. In these cases you'll need to fall back to more traditional means of testing.

How To, Tutorial, and Recipies

There are two ways to use the framework. They are standalone and as a unittest.TestCase. For standalone you need to create a controller. As a test case the controller is implicitly created.

Standalone:

from pymock import *
c = Controller()
m = c.mock()
From within a unittest:

from pymock import *
class MyTestCase(PyMockTestCase):

	def testMethod(self):
		m = self.mock()
I'll assume for the rest of the examples that we're using PyMockUnitTestCase. PyMock uses a record and playback model. The controller starts in record mode, and you have to tell it that you're changing to playback mode. Once you've played back all the methods you can verify that this has happened as expected by calling the controller's verify method.

This example records, replays, and verify that a method was called.

def testMethod(self):
	m = self.mock()
	m.function()
	self.replay()
	m.function()
	self.verify()
But function calls aren't particularly useful unless you can return a value. This example shows a value being returned.

def testMethod(self):
	m = self.mock()
	m.function(5)
	self.setReturn(6)
	self.replay()
	self.failUnless(m.function(5) == 6)
	self.verify()
You can condense the calls a little bit with expectAndReturn(self, x, y).

def testMethod(self):
	m = self.mock()
	self.expectAndReturn(m.function(5), 6)
	self.replay()
	self.failUnless(m.function(5) == 6)
	self.verify()
Exceptions can be thrown in a similar manner.

def testMethod(self):
	m = self.mock()
	m.function(5)
	self.raiseException(Exception())
	self.replay()
	try:
		m.function(5)
		self.fail()
	except Exception, e:
		pass
	self.verify()
Exceptions can be condensed too.

def testMethod(self):
	m = self.mock()
	self.expectAndRaise(m.function(5), Exception())
	self.replay()
	try:
		m.function(5)
		self.fail()
	except Exception, e:
		pass
	self.verify()
Getting properties works in pretty much exactly the same way as making function calls. Throwing exceptions works the same way too.

def testMethod(self):
	m = self.mock()
	m.g
	self.setReturn(7)
	self.replay()
	self.failUnless(m.g == 7)
	self.verify()
Setting works too, but setReturn doesn't make any sense in this context. However you can use setException to raise exceptions, but since property setting is an a statement and not an expression you're limited to using setReturn. The expectAndRaise method just won't work for this case.

def testMethod(self):
	m = self.mock()
	m.g = 8
	self.replay()
	m.g = 8
	self.verify()
Until you call setReturn on a function or a property get you are in possession of another mock object. You can construct references to chains of mock objects simply by making the calls on that object.

def testMethod(self):
	m = self.mock()
	x = m.f(8, 9)
	x.g
	x.g.h(7)
	self.replay()
	x = m.f(8, 9)
	x.g
	x.g.h(7)	
	self.verify()
You can turn any mock function call into a mock generator using the generator method.

def testMethod(self):
	m = self.mock()
	self.generator(m.f(), [1, 2, 3])
	g = m.f()
	self.replay()
	self.failUnless(g.next() == 1)
	self.failUnless(g.next() == 2)
	self.failUnless(g.next() == 3)
	self.failUnlessRaises(StopIteration, g.next)
You can tell generator to terminate any sequence with an arbitrary exception.

def testMethod(self):
	m = self.mock()
	self.generator(m.f(), [1, 2], Exception())
	self.replay()
	g = m.f()
	self.failUnless(g.next() == 1)
	self.failUnless(g.next() == 2)
	self.failUnlessRaises(Exception, g.next)
The mocked __iter__() method is different from others. It is automatically turned into a generator. You can set its return values by using the generator call or by repeatedly calling setReturn and, if wanted, by finally calling setException.

def testMethod(self):
	m = self.mock()
	self.__iter__()
	self.setReturn(1)
	self.setReturn(2)
	self.replay()
	g = m.f()
	self.failUnless(g.next() == 1)
	self.failUnless(g.next() == 2)
	self.failUnlessRaises(StopIteration, g.next)
So far every mocked call has been used exactly once. Any more or any less results in an error when run or verified. This need not be the case.

There are a numer of calls to alter the playback behavior. They are setCount(x), atLeast(x), zeroOrMore(), and oneOrMore(). These calls are used in a manner analogous to setReturn or setException.

The meanings are straight forward. setCount(x) says that the preceeding call should be replayed precisely x times. atLeast(x) says that the preceeding call should be replayed at least x times, but may be replayed more. zeroOrMore() says the preceeding call can be replayed any number of times, and finally oneOrMore() says the preceeding call must by replayed at least once, but can be replayed more often.

def testMethod(self):
	m = mock()
	expectAndReturn(m.f(), 2)
	self.setCount(2)
	expectAndReturn(m.f(), 3)
	self.oneOrMore()
	self.replay()
	self.failUnless(m.f() == 2)
	self.failUnless(m.f() == 2)
	for x in range(0, 200):
		self.failUnless(m.f() == 3)
	self.verify()
There is one complication arising from these. atLeast, zeroOrMore, and oneOrMore specify an indefinite and potentially infinite number of playbacks. Once you set one of these specifiers you can't record the preceeding call any more. You'll receive an error if you try to do so.

Now comes the fun stuff. This is what makes PyMock different from easymock or other java mocking systems. You're not limited to overriding mock objects. You can mock out individual methods and fields in any sort of object. When your code completes the mocked method, property, or function vanishes.

Here's how you'd mock out calls to os.listdir.

def testMethod(self):
	self.override(os, 'listdir') # object, method name
	expectAndReturn(os.listdir(), ['one', 'two'])
	self.replay()
	self.failUnless(os.listdir() == ['one', 'two'])
	self.verify()
Properties are a little different. They need a special declaration since they're handled in a very different way. (Hmmmm... I may have figured out an obvious way around that, but I want to get this out the door first.) Here's how you'd mock out calls to a property. Note that unlike other calls, all the calls to an overridden property must be played back in order.

def testMethod(self):
	self.overrideProperty(os, 'separator')
	os.separator = '\\'
	expectAndReturn(os.separator, '\\')
	del os.separator
	self.replay()
	os.separator = '\\'
	self.failUnless(os.separator == '\\')
	del os.separator
	self.verify()
A final note. If you're using PyMockTestCase then you must ensure the inherited setUp and tearDown methods are called. An example of code that does this follows.

class TestSetUpTeardownDemonstration(PyMockTestCase):
	
	def setUp(self):
		super(TestSetUpTearDownDemonstration, self).setUp()
		... do local setup ...
	
	def tearDown(self):
		super(TestSetUpTearDownDemonstration, self).tearDown()
		... do local tear down ...
		
	def testMethod(self):
		... do test ...
An Example Using Controllers vs. Using a Test Case

You can use the controllers directly or you can use the ready made test case object. Both are shown below, along with the code to be tested.

class X(object):
	def write_string_to_file(self, f, str):
		f.write(str)
Here's the test case:

class TestX(TestCase):
	def test_write_string_to_channel(self):
		test_string = "test_string"
		c = Controller()
		f = c.mock() # make a mock object
		f.write(test_string) # this is the function call we expect
		c.replay() # tell the controller that we're going to play back the recorded call
		x = X()
		x.write_string_to_file(f, test_string) # perform the call
		x.verify() # check that writer.write(test_string) was called
class TestX(PyMockTestCase):
	def test_write_string_to_channel(self):
		test_string = "test_string"
		mock_file = self.mock()
		mock_file.write(test_string)
		self.replay()
		x = X()
		x.write_string_to_file(f, test_string)
		self.verify()
The important thing to notice in the first case is the controller object. It keeps track of all the mock object states. The PyMockTestCase automatically constructs a controller for you.