Reputation: 111
I want to be able to see how classes are instantiated and I want to see how how methods of that class are used. I can accomplish the first goal, but, the code below demonstrates how I can't spy on the method calls. The final assert fails.
import mock
class A:
def __init__(self, some_arg):
print("constructor")
def f(self, some_var):
print(some_var)
p = mock.patch('__main__.A', wraps=A)
m = p.start()
A = m
a = A('something')
a.f('my_arg')
assert mock.call('something') in m.mock_calls
assert m.method_calls # This fails, call to f is not tracked
If I use autospec=True I can see the method calls, but then the actual method isn't called. I want the actual code to run, I just want to spy on it.
I can't do something like http://wesmckinney.com/blog/spying-with-python-mocks/ because I don't have an instance of the class.
Upvotes: 4
Views: 2359
Reputation: 111
This https://stackoverflow.com/a/41599695/9816369 has a pretty solid solution. From that, I can do this:
import mock
def spy_decorator(method_to_decorate):
m = mock.MagicMock()
def wrapper(self, *args, **kwargs):
m(*args, **kwargs)
return method_to_decorate(self, *args, **kwargs)
wrapper.mock = m
return wrapper
class A:
def __init__(self, some_arg):
print("constructor")
def f(self, some_var):
print(some_var)
construct_spy = spy_decorator(A.__init__)
f_spy = spy_decorator(A.f)
p_construct = mock.patch('__main__.A.__init__', construct_spy)
p_f = mock.patch('__main__.A.f', f_spy)
m_construct = p_construct.start()
m_f = p_f.start()
a = A("hi")
a.f("car")
m_construct.mock.assert_called_once_with("hi")
m_f.mock.assert_called_once_with("car")
It could be a bit nicer, but this is pretty solid. I should also mention that there is https://github.com/beanbaginc/kgb but I didn't want to modify the requirements file I'm working with.
Upvotes: 4