I'm just starting out using the python mocking framework. I'd like to just count the number of times a method gets called, without removing the effects of actually calling the method.
For example, in this simple counter example, I would like to both increment the counter and track that it was called:
import unittest
import mock
class Counter(object):
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
class CounterTest(unittest.TestCase):
def test_increment(self):
c = Counter()
c.increment()
self.assertEquals(1, c.count)
def test_call_count(self):
with mock.patch.object(Counter, 'increment') as fake_increment:
c = Counter()
self.assertEquals(0, fake_increment.call_count)
c.increment()
self.assertEquals(1, fake_increment.call_count)
# increment() didn't actually get called.
self.assertEquals(1, c.count) # Fails.
if __name__ == '__main__':
unittest.main()
Is it possible to force mock
to call the mocked method after it registered the call, or just signify that I want to keep the effects of the mocked function?
How do we mock in Python? Mocking in Python is done by using patch to hijack an API function or object creation call. When patch intercepts a call, it returns a MagicMock object by default. By setting properties on the MagicMock object, you can mock the API call to return any value you want or raise an Exception .
call_count is used to see how many times the method is called. . call_args tells the arguments used when the method is called.
side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT , the return value of this function is used as the return value.
Mock vs. So what is the difference between them? MagicMock is a subclass of Mock . It contains all magic methods pre-created and ready to use (e.g. __str__ , __len__ , etc.). Therefore, you should use MagicMock when you need magic methods, and Mock if you don't need them.
Just use wraps:
c = Counter()
with mock.patch.object(Counter, 'increment', wraps=c.increment) as fake_increment:
There can be some binding problems if you initialize c
later, as the function passed to wraps
won't know about self
.
I'm not super experienced in mock1, but I accomplished it by using a function wrapper rather than the default MagicMock
:
class FuncWrapper(object):
def __init__(self, func):
self.call_count = 0
self.func = func
def __call__(self, *args, **kwargs):
self.call_count += 1
return self.func(*args, **kwargs)
class CounterTest(unittest.TestCase):
def test_call_count(self):
c = Counter()
new_call = FuncWrapper(c.increment)
with mock.patch.object(c, 'increment', new=new_call) as fake_increment:
print fake_increment
self.assertEquals(0, fake_increment.call_count)
c.increment()
self.assertEquals(1, fake_increment.call_count)
self.assertEquals(1, c.count) # Fails.
Of course, this FuncWrapper
is pretty minimal. It just counts the calls and then delegates flow control back to the original function. If you need to test other things at the same time, you'd need to add to the FuncWrapper
class. I've also just patched a class instance rather than the entire class. The main reason for that is because I needed an instance method in FuncWrapper
.
1In fact, I just started to learn -- Consider yourself warned ;-).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With