Or... My adventures with Mock. Part 1.
I've been working a lot with Mock lately (and by lately, I meand for the last three months). Though it takes a while to wrap your head around it, it's an amazing and powerful testing tool.
To get started, let's look at some of the neat things you can do with Mock. Take this class, for example:
class Thing(object): shape = 'square' color = 'blue' def calculate(self): # ... do some stuff ...
If you were writing a test, and you wanted a Mocked
still wanted to ensure that your mock object had
color attributes, you'd do the following:
>>> from mock import Mock >>> thing = Mock(shape='square', color='blue') >>> thing.shape 'square' >>> thing.color 'blue'
Cool! You get attributes whose values you can test against, and you still have a mock object on which you can call methods:
>>> from mock import Mock >>> thing.calculate() # pretend this calculates something <Mock name='mock.calculate()' id='4338034768'>
I think Mock really shines when you're working with code that hits external
APIs (I do this a lot with
Work for Pie). Imagine for a minute that
Thing.calculate() sent some data up to an external API, then
used the results to calculate and return a value. With a Mocked object, your
tests can run without hitting the api. This is a Good Thing! In order to write
that test, you'd do somethign like this
from mock import calls def test_calculate(): thing = Mock(shape='square', color='blue') thing.calculate() thing.assert_has_calls([call.calculate()])
So what was that thing about
Now here's where things get tricky. For one reason or another, many of my
API-wrapper classes have a
name attribute. The Mock class also
has a keyword argument
name, that lets you give the Mock a name
(the docs say this is useful for debugging).
So, how in the world am I supposed to write a Mock for something like this, and still be able to specify the value of an attribute?
class SomeAPIWrapper(object): name = 'brad' token = 'secret'
Well, this does not work:
>>> api = Mock(name='brad', token='secret') >>> api.token # Ok, this looks fine. 'secret' >>> api.name # not what I want <Mock name='important-thing-here.name' id='4337316944'>
Luckily, there's this neat class called a
PropertyMock. It took me a bit to figure out how to use it, but it's
essentially used as a property or an attribute on another class (including
another mock). I honestly don't know if this is supposed to work this
way or if this is a nasty hack (feel free to let me
know one way or another), but this is how I attached a
attribute to a Mock object:
>>> from mock import Mock, PropertyMock >>> # Create a Mock for my ``SomeAPIWrapper`` class above >>> api = Mock(token='secret') >>> # Create a PropertyMock representing the ``name`` attribute >>> p = PropertyMock(return_value='brad') >>> # Replace Mock's name with our new property >>> type(api).name = p >>> >>> # Now see what happens >>> api.token 'secret' >>> api.name 'brad'
Now, your Mock objects can have a name attribute with an expected return value.
comments powered by Disqus