Using Python mock to spy on calls to an existing object - python

I'm using the Python mock module for tests. I would like to replace an active object with a mock, and automatically have all calls made to the mock object forwarded to the original object. I think this is called a "Spy" in standard testing terminology. At the moment I'm doing inside a test:
# Insert a mock replacement
orig_active_attr = server.active_attr
server.active_attr = mock.Mock()
# Set up side effects to 'proxy' to the original object
server.active_attr.meth1.side_effect = orig_active_attr.meth1
server.active_attr.meth2.side_effect = orig_active_attr.meth2
# Call the method being tested
server.method_being_tested()
# Assert stuff on the mock.
server.active_attr.meth2.assert_called_once()
It would be nice if all method calls on the mock could be forwarded to the live object automatically without the boilerplate.

I seem to have stumbled across the solution:
import mock
class A(object):
def meth(self, a):
return a
a = A()
ma = mock.Mock(wraps=a)
Seems to work okay for functions, methods and properties, but not for class or instance attributes.
See the documentation.

You can use patch.object(wraps=obj_instance) as suggested in Spying on instance methods with Python's mock module.
For example:
from mock import patch
class Foo(object):
def bar(self, x, y):
return x + y + 1
def test_bar():
foo = Foo()
with patch.object(foo, 'bar', wraps=foo.bar) as wrapped_foo:
foo.bar(1, 2)
wrapped_foo.assert_called_with(1, 2)

Here's how to mock only datetime.date.today(), forwarding the rest of datetime calls to the datetime module:
from unittest import mock, TestCase
import foo_module
class FooTest(TestCase):
#mock.patch(f'{foo_module.__name__}.datetime', wraps=foo_module.datetime)
def test_something(self, mock_datetime):
# mock only datetime.date.today()
mock_datetime.date.today.return_value = datetime.date(2019, 3, 15)
# other calls to datetime functions will be forwarded to original datetime
foo_module imports datetime and uses many other datetime functions besides date.today.

You can use a simple function to iterate through all the method and configure your mock
def spy_mock(instance):
members = inspect.getmembers(instance, inspect.ismethod)
attrs = {'%s.side_effect' % k:v for k,v in members}
return mock.Mock(**attrs)
Usage would be
import inspect
from unittest import mock
class ActiveAttr:
def meth2(self):
print("Meth2 called")
class Server:
def __init__(self):
self.active_attr = ActiveAttr()
def method_being_tested(self):
self.active_attr.meth2()
def spy_mock(instance):
members = inspect.getmembers(instance, inspect.ismethod)
attrs = {'%s.side_effect' % k:v for k,v in members}
return mock.Mock(**attrs)
server = Server()
server.active_attr = spy_mock(server.active_attr)
server.method_being_tested()
server.active_attr.meth2.assert_called_once()

Extending upon the pattern from Wes McKinney (via Wilfred Hughes Answer), here is how to spy on a method/member of an object, where object is imported into module under test.
Note this solution is Python 2.x compliant!
module under test:
import object
def function_in_module_under_test():
object.method_from_imported_object()
testing spied assertion on method_from_imported_object:
from unittest import mock
import module_under_test
def test_method(self):
with mock.patch.object(
module_under_test.object,
"method_from_imported_object",
module_under_test.object.method_from_imported_object,
) as spy_method_from_imported_object:
# Demonstrate that subsequent spy asserts here, can be trusted
spy_method_from_imported_object.assert_not_called()
# now Test
module_under_test.function_in_module_under_test()
spy_method_from_imported_object.assert_called_once()

If you use pytest, the pytest-mock package has a convenient spy object.
class Foo(object):
def bar(self, v):
return v * 2
def test_spy_method(mocker):
foo = Foo()
spy = mocker.spy(foo, 'bar')
assert foo.bar(21) == 42
spy.assert_called_once_with(21)
assert spy.spy_return == 42

Related

How to Mock a class and its methods which are used by another .py

I am trying to mock a class and its method that is used by another class.
My file structure is:
[1] my_animal.py contains Myclass and method: do_bite()
my_animal.py
class Myclass():
def do_bite(self):
return 1
[2] my_module.py contains jobMain("") which is using the method from my_animal.py
my_module.py
import sys
from my_animal import Myclass
def jobMain(directoryPath):
result = Myclass()
if result.do_bite() is None:
sys.exit(1) # stop here
[3] my_test.py contains the unittest to test jobMain in my_module.py
my_test.py
from my_animal import Myclass
# Try to mock Myclass
#pytest.fixture
def mock_Myclass(monkeypatch):
""" Mock myclass """
monkeypatch.setattr(my_module, "Myclass", MagicMock())
# I tried below code to mock dobite() method, but it was unsuccessfully
# Mock Myclass.dobite to None"""
#pytest.fixture
def mock_dobite(monkeypatch):
def mock_return(*args, **kwargs):
with patch('my_animal',dobite) as p:
instance = p.return_value
instance.dobite = Mock(return_value = None)
monkeypatch.setattr(my_module.Myclass, "do_bite", mock_return )
# My unittest to test dobite() method
def test_dobite(mock_Myclass, mock_dobite):
with pytest.raises(SystemExit) as s_exit:
jobMain("")
assert s_exit.type == SystemExit
assert s_exit.value.code == 1
My question is: How could I mock the return value of the method do_bite() to None or any other expected value?
Your fixture is overly complicated, here is what you can use:
import pytest
from unittest import mock
#pytest.fixture
def mock_dobite():
with mock.patch('my_module.Myclass') as mocked_animal:
mocked_animal.return_value.do_bite.return_value = None
yield
You patch the class as it is imported (see where to patch). Because Myclass is imported like this:
from my_animal import Myclass
you have a local reference to the module, which you have to patch.
mocked_animal is the mocked class. To get the instance of the class, you have to use mocked_animal.return_value. By adding do_bite.return_value, you now can set the return value of the method do_bite.
Now your test will work like this (note that you need only one fixture parameter):
def test_dobite(mock_dobite):
with pytest.raises(SystemExit) as s_exit:
jobMain("")
assert s_exit.type == SystemExit
assert s_exit.value.code == 1

How to mock nested function calls of the same class [duplicate]

Is there a clean way to patch an object so that you get the assert_call* helpers in your test case, without actually removing the action?
For example, how can I modify the #patch line to get the following test passing:
from unittest import TestCase
from mock import patch
class Potato(object):
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
#patch.object(Potato, 'foo')
def test_something(self, mock):
spud = Potato()
forty_two = spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
I could probably hack this together using side_effect, but I was hoping there would be a nicer way which works the same way on all of functions, classmethods, staticmethods, unbound methods, etc.
Similar solution with yours, but using wraps:
def test_something(self):
spud = Potato()
with patch.object(Potato, 'foo', wraps=spud.foo) as mock:
forty_two = spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
According to the documentation:
wraps: Item for the mock object to wrap. If wraps is not None then
calling the Mock will pass the call through to the wrapped object
(returning the real result). Attribute access on the mock will return
a Mock object that wraps the corresponding attribute of the wrapped
object (so attempting to access an attribute that doesn’t exist will
raise an AttributeError).
class Potato(object):
def spam(self, n):
return self.foo(n=n)
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
def test_something(self):
spud = Potato()
with patch.object(Potato, 'foo', wraps=spud.foo) as mock:
forty_two = spud.spam(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
This answer address the additional requirement mentioned in the bounty from user Quuxplusone:
The important thing for my use-case is that it work with #patch.mock, i.e. that it not require me to insert any code in between my constructing of the instance of Potato (spud in this example) and my calling of spud.foo. I need spud to be created with a mocked-out foo method from the get-go, because I do not control the place where spud is created.
The use case described above could be achieved without too much trouble by using a decorator:
import unittest
import unittest.mock # Python 3
def spy_decorator(method_to_decorate):
mock = unittest.mock.MagicMock()
def wrapper(self, *args, **kwargs):
mock(*args, **kwargs)
return method_to_decorate(self, *args, **kwargs)
wrapper.mock = mock
return wrapper
def spam(n=42):
spud = Potato()
return spud.foo(n=n)
class Potato(object):
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(unittest.TestCase):
def test_something(self):
foo = spy_decorator(Potato.foo)
with unittest.mock.patch.object(Potato, 'foo', foo):
forty_two = spam(n=40)
foo.mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
if __name__ == '__main__':
unittest.main()
If the method replaced accepts mutable arguments which are modified under test, you might wish to initialize a CopyingMock* in place of the MagicMock inside the spy_decorator.
*It's a recipe taken from the docs which I've published on PyPI as copyingmock lib
For those who don't mind using side_effect, here's a solution with a few pros:
Uses decorator syntax
Patches an unbound method, which I find more versatile
Requires inclusion of the instance in the assertion
class PotatoTest(TestCase):
#patch.object(Potato, 'foo', side_effect=Potato.foo, autospec=True)
def test_something(self, mock):
spud = Potato()
forty_two = spud.foo(n=40)
mock.assert_called_once_with(spud, n=40)
self.assertEqual(forty_two, 42)
You are describing the same question than Python mock: wrap instance method. My solution in https://stackoverflow.com/a/72446339/9230828 can be applied as following: Put wrap_object somewhere, e.g. into wrap_object.py:
# Copyright (C) 2022, Benjamin Drung <bdrung#posteo.de>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import contextlib
import typing
import unittest.mock
#contextlib.contextmanager
def wrap_object(
target: object, attribute: str
) -> typing.Generator[unittest.mock.MagicMock, None, None]:
"""Wrap the named member on an object with a mock object.
wrap_object() can be used as a context manager. Inside the
body of the with statement, the attribute of the target is
wrapped with a :class:`unittest.mock.MagicMock` object. When
the with statement exits the patch is undone.
The instance argument 'self' of the wrapped attribute is
intentionally not logged in the MagicMock call. Therefore
wrap_object() can be used to check all calls to the object,
but not differentiate between different instances.
"""
mock = unittest.mock.MagicMock()
real_attribute = getattr(target, attribute)
def mocked_attribute(self, *args, **kwargs):
mock.__call__(*args, **kwargs)
return real_attribute(self, *args, **kwargs)
with unittest.mock.patch.object(target, attribute, mocked_attribute):
yield mock
Then you can write following unit test:
from unittest import TestCase
from wrap_object import wrap_object
class Potato:
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
def test_something(self):
with wrap_object(Potato, 'foo') as mock:
spud = Potato()
forty_two = spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
I did it with a bit another way because IMO mocking is preferable over patching
from unittest.mock import create_autospec
mocked_method = create_autospec(
spec=my_method,
spec_set=True,
# Will implement a real behavior rather than return a Mock instance
side_effect=*a, **kw: my_method.do_something(*a, **kw))
mocked_object.do_something()
mocked_object.assert_called_once()

Mocking Methods within Classes

I've read a lot of information online about mocking entire classes. However, I have a class with multiple methods; for example:
class A():
def methoda(param1, param2):
do things
return thing
def methodb(param3):
do things (including something calling methoda)
How can I mock out methoda in a testing file to return a desired value so that I can test methodb? I don't want to mock out the entire class.
What I've tried doing:
from mock import patch, mock, MagicMock
from sourceA.models import ClassName
from django.test import TestCase
class ClassTest(TestCase):
#patch('sourceA.models.ClassName.methodA')
def test_method(self, mock_method_return):
mock_method_return.return_value = 10
instance = ClassName()
instance.methodB #METHOD B CALLS METHOD A; I want method A to return 10
print "OUTPUT", instance.FIELDA #Debug Tool - Field A is modified by Method B, based on the return value of method A
assert instance.FIELDA == 10
I have also tried this:
class ClassTest(TestCase):
#patch('sourceA.models.ClassName.methodA', return_value=10)
def test_method(self, mock_method_return):
instance = Mock(spec=ClassName())
instance.methodB #METHOD B CALLS METHOD A; I want method A to return 10
------ OR instance methodB = MagicMock(return_value=10) and then instance.MethodB
print "OUTPUT", instance.FIELDA #Debug Tool - Field A is modified by Method B, based on the return value of method A
self.assertEqual(instance.FIELDA, 10)
I'm not sure what else I should try/what I am doing wrong.

Patch a method outside python class

I am interested in patching a method which is called by another method in one file. Example - original.py file contains -
def A():
a = 10
b = 5
return a*b;
def B():
c = A()
return c* 10
I want to write unit test for this file , say call it test.py
import mock
import unittest
class TestOriginal(unitest.TestCase):
def test_Original_method(self):
with patch(''):
How can I use patch and mock modules to test original.py. I want A() to always return MagicMock() object instead of an integer.
You simply patch out the A global in the module under test. I'd use the #patch decorator syntax here:
import mock
import unittest
import module_under_test
class TestOriginal(unitest.TestCase):
#patch('module_under_test.A')
def test_Original_method(self, mocked_A):
mocked_A.return_value = 42
result = module_under_test.B()
mocked_A.assert_called_with()
self.assertEqual(result, 420)
This passes in the MagicMock mock object for A() as an extra argument to the test method.
Note that we explicitly named the module here. You could also use patch.object(), just naming the attribute on the module (which are your module globals):
class TestOriginal(unitest.TestCase):
#patch.object(module_under_test, 'A')
def test_Original_method(self, mocked_A):
mocked_A.return_value = 42
result = module_under_test.B()
mocked_A.assert_called_with()
self.assertEqual(result, 420)
You can still use a with statement too, of course:
class TestOriginal(unitest.TestCase):
def test_Original_method(self):
with patch('module_under_test.A') as mocked_A:
mocked_A.return_value = 42
result = module_under_test.B()
mocked_A.assert_called_with()
self.assertEqual(result, 420)

How do you mock patch a python class and get a new Mock object for each instantiation?

OK,
I know this is mentioned in the manual, and probably has to do with side_effect and/or return_value, but a simple, direct example will help me immensely.
I have:
class ClassToPatch():
def __init__(self, *args):
_do_some_init_stuff()
def some_func():
_do_stuff()
class UUT():
def __init__(self, *args)
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
Now, I want to unit test the UUT class, and mock the ClassToPatch. Knowing the UUT class will instantiate exactly two ClassToPatch objects, I want the Mock framework to return a new Mock object for each instantiation, so I can later assert calls on each separately.
How do I achieve this using the #patch decorator in a test case? Namely, how to fix the following code sample?
class TestCase1(unittest.TestCase):
#patch('classToPatch.ClassToPatch',autospec=True)
def test_1(self,mock1,mock2):
_assert_stuff()
Here's a quick'n'dirty example to get you going:
import mock
import unittest
class ClassToPatch():
def __init__(self, *args):
pass
def some_func(self):
return id(self)
class UUT():
def __init__(self, *args):
resource_1 = ClassToPatch()
resource_2 = ClassToPatch()
self.test_property = (resource_1.some_func(), resource_2.some_func())
class TestCase1(unittest.TestCase):
#mock.patch('__main__.ClassToPatch', autospec = True)
def test_1(self, mock1):
ctpMocks = [mock.Mock(), mock.Mock()]
ctpMocks[0].some_func.return_value = "funky"
ctpMocks[1].some_func.return_value = "monkey"
mock1.side_effect = ctpMocks
u = UUT()
self.assertEqual(u.test_property, ("funky", "monkey"))
if __name__ == '__main__':
unittest.main()
I've added test_property to UUT so that the unit test does something useful. Now, without the mock test_property should be a tuple containing the ids of the two ClassToPatch instances. But with the mock it should be the tuple: ("funky", "monkey").
I've used the side_effect property of the mock object so that a different instance of ClassToPatch is returned on each call in the UUT initialiser.
Hope this helps.
Edit: Oh, by the way, when I run the unit test I get:
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Here is another version which is more generic to handle any number of instances created:
class TestUUT:
def test_init(self, mocker):
class MockedClassToPatchMeta(type):
static_instance = mocker.MagicMock(spec=ClassToPatch)
def __getattr__(cls, key):
return MockedClassToPatchMeta.static_instance.__getattr__(key)
class MockedClassToPatch(metaclass=MockedClassToPatchMeta):
original_cls = ClassToPatch
instances = []
def __new__(cls, *args, **kwargs):
MockedClassToPatch.instances.append(
mocker.MagicMock(spec=MockedClassToPatch.original_cls))
MockedClassToPatch.instances[-1].__class__ = MockedClassToPatch
return MockedClassToPatch.instances[-1]
mocker.patch(__name__ + '.ClassToPatch', new=MockedClassToPatch)
UUT()
# since your original code created two instances
assert 2 == len(MockedClassToPatch.instances)
If you need more thorough validation for each instance you can access MockedClassToPatch.instances[0] or MockedClassToPatch.instances[1].
I've also created a helper library to generate the meta class boilerplate for me. To generate the needed code for your example I wrote:
print(PytestMocker(mocked=ClassToPatch, name=__name__).mock_classes().mock_classes_static().generate())

Categories