Saturday 12 June 2010

Mock recipes

I posted recently a simple introduction to the mock library. Today, I'd like to show you some of its indirect uses, that I found helpful in my everyday testing needs.

Custom patcher

It is very easy to set up mock objects to respond to events. You just access attributes and set either them or their return value. When using the patch decorator, a mock instance is provided to your test method as the last argument, so you set it up just before excercising your production code.

@mock.patch("sys.stdin")
def test_stdin_reader(mock_stdin):
mock_stdin.write.return_value = "hello" # setting up mock_stdin
run_code_under_test()
validate_results()

In this example the setting up part is really easy - just a single line. But in real-life projects, you might find yourself setting whole attribute hierarchies on mocks, repeatedly in every many test methods.

That's when a custom patcher allows you to implement the DRY principle in an elegant manner.

class MyTest(unittest.TestCase):    
@patch_db
def test_one(self):
pass
@patch_db
def test_two(self):
pass

It's very easy to write a custom patcher.

def patch_db(func):    
@patch("db.module")
def wrapper(*args):
mock_db = args[-1]
# ... perform complicated setup on mock_db
func(*args[:-1])
return wrapper


Cutting off

The cut_off patcher is useful when you need to mock out several objects, and don't care about the mock instances. Say you want to deactivate internet access in a module that uses both urllibs.

@cut_off("mymodule.urllib", "mymodule.urllib2")
def test_something():
pass

The implementation of cut_off is very simple too.

def cut_off(*patches):    
def decorator(meth):
def wrapper(self, *args):
return meth(self, *args[:-len(patches)])
for obj in patches:
wrapper = mock.patch(obj)(wrapper)
return wrapper
return decorator

No comments: