unittest mock replace/reset mocked function when patching an object - python

I need to use unittest.mock.patch.object to mock an external method that may fail sometimes. In the test, the method shall raise some errors and then return to the original behaviour. Note that the behaviour I want to reproduce is by far more complicated than return 'bar' so I cannot just copy the code in Bar.some_method_that_may_fail:
import unittest
from unittest.mock import patch
class Bar(object):
def some_method_that_may_fail(self):
return "bar"
class Foo(object):
bar = None
def retry_method(self):
try:
self.__class__.bar = Bar().some_method_that_may_fail()
except KeyError:
self.retry_method()
class TestRetry(unittest.TestCase):
def setUp(self):
self.instance = Foo()
def test_retry(self):
# raise KeyError the first 5 calls to the function and then work normally
errors_list = [KeyError("")] * 5
def raise_errors(*_):
if errors_list:
errors_list.pop(0)
# TODO: return to original behaviour
with patch.object(Bar, 'some_method_that_may_fail', new=raise_errors) as mocked:
self.instance.retry_method()
self.assertEqual(self.instance.bar, 'bar')
if __name__ == '__main__':
unittest.main()

To return different values on subsequent invocations, you can use side_effect. Passing an array of values and/or exceptions will return these values/raise these exceptions in subsequent calls, in your case the exception instances and the result of an original call (if that is what you need).
So your test could look something like this:
class TestRetry(unittest.TestCase):
def setUp(self):
self.instance = Foo()
def test_retry(self):
original = Bar.some_method_that_may_fail # save the original
with patch(__name__ + '.Bar') as mocked:
bar = mocked.return_value
side_effect = ([KeyError()] * 5) + [original(bar)]
bar.some_method_that_may_fail.side_effect = side_effect
self.instance.retry_method()
self.assertEqual(6, mocked.call_count)
self.assertEqual('bar', self.instance.bar)
A few notes:
I replaced mock.patch.object with mock.patch, because you don't have an object to patch (Bar is instantiated inside the tested function, and you need to patch the instance, not the class)
using __name__ + '.Bar' for patching is because the tested function is in the same module as the test - in the real code this has to be replaced with the correct module path
I added a check for call_count to ensure that the method has indeed been called 6 times
Another thing: you have an error in your Foo, probably because you dumbed it down for the example. foo is a class variable, but you are setting an instance variable of the same name. You need instead:
class Foo:
bar = None
def retry_method(self):
try:
self.__class__.bar = Bar().some_method_that_may_fail()
except KeyError:
self.retry_method()
(note the self.__class__.bar)

Related

Mock patch the return value of an inner function that's not returned from the outer function

I'd like to capture the return value of an inner function without having to explicitly return that return value from the outer function.
So I'd like to do something like this:
# bar.py
import foo
def my_fn():
foo.fn()
# test.py
from mock import patch
import foo
import bar
#patch("foo.fn")
def test_bar(mock_foo_fn):
bar.my_fn()
# assert isinstance(mock_foo_fn.return_value, dict)
without having to do this:
# bar.py
import foo
def my_fn():
return foo.fn()
I have a cleaner solution, using a spy.
The mocker.spy object acts exactly like the original method in all cases, except the spy also tracks function/method calls, return values and exceptions raised.
The spy is provided from the mocker fixture of the plugin pytest-mock. Usage demo for your example:
# test.py
import pytest
import bar
def test_bar(mocker):
spy = mocker.spy(bar.foo, "fn")
bar.my_fn()
spy.assert_called_once_with()
assert spy.spy_return == {"k": "v"}
This might be more complicated than necessary, but I think you need to patch foo.fn with a callable object that capture the return value of the original function, and not just a Mock. Something like
class Wrapper:
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
self.rv = self.f(*args, **kwargs)
with patch('foo.fn', new=Wrapper(foo.fn)) as m:
my_fn()
assert isinstance(m.rv, dict)
(Using something like with patch('foo.fn', wraps=foo.fn) is not sufficient, because while that ensures that foo.fn itself is called when the mock is called, the mock itself doesn't not capture the resulting return value.)

How to set "return_value" once for TestCase. Python. Django

Here is example test:
import a
import b
import c
import mock
from django.test import TestCase
#mock.patch.object(a, "method_a")
#mock.patch.object(b, "method_b")
#mock.patch.object(c, "method_c")
class SomeTestCase(TestCase):
def setUp(self):
# I want to set mock_method_a.return_value = 1 once here (or not here, but once)
pass
def test_one(self, mock_method_a, mock_method_b, mock_method_c):
mock_method_a.return_value = 1
mock_method_b.return_value = 2
pass # some test stuff
def test_two(self, mock_method_a, mock_method_b, mock_method_c):
mock_method_a.return_value = 1
mock_method_b.return_value = 2
pass # some test stuff
def test_three(self, mock_method_a, mock_method_b, mock_method_c):
mock_method_a.return_value = 1
mock_method_b.return_value = 2
pass # some test stuff
Queston:
How I can avoid of duplicate code for setting "return_value" in each test in TestCase?
I expect something in "setUp" method or something similar.
Is it possible?
PS: mock version mock==1.3.0, django version Django==1.8.4
You can set the return_value right there in the #mock.patch.object() decorators:
#mock.patch.object(c, "method_c", return_value=3)
#mock.patch.object(b, "method_b", return_value=2)
#mock.patch.object(a, "method_a", return_value=1)
class SomeTestCase(TestCase):
def test_one(self, mock_method_a, mock_method_b, mock_method_c):
# do test stuff, return values have been set
def test_two(self, mock_method_a, mock_method_b, mock_method_c):
# do test stuff, return values have been set
def test_three(self, mock_method_a, mock_method_b, mock_method_c):
# do test stuff, return values have been set
(Note: when decorating with #mock.patch the decorators are applied from the bottom on up, so for mock_method_a to be passed in as the first argument you need to put the decorator closest to the class definition).
The return_value keyword argument to mock.patch.object() is passed to the MagicMock() constructor. See the mock.patch.object() documentation:
Like patch(), patch.object() takes arbitrary keyword arguments for configuring the mock object it creates.
and the mock.Mock documentation:
Mock takes several optional arguments that specify the behaviour of the Mock object:
[...]
return_value: The value returned when the mock is called. By default this is a new Mock (created on first access). See the return_value attribute.
If you also want to avoid setting the mocks outside of your test case or don't like the additional arguments to each test function, then you you can also can create patchers in the setUp method, which then are removed again when the test ends by registering a callback via the unittest.TestCase.addCleanup() method.
The patchers are applied for each test, by calling the patcher.start() methods, which returns the new mock object:
class SomeTestCase(TestCase):
def setUp(self):
patcher_method_a = mock.patch.object(a, "method_a")
self.mock_method_a = patcher_method_a.start()
self.mock_method_a.return_value = 1
patcher_method_b = mock.patch.object(b, "method_b")
self.mock_method_b = patcher_method_b.start()
self.mock_method_b.return_value = 2
patcher_method_c = mock.patch.object(c, "method_c")
self.mock_method_c = patcher_method_c.start()
self.mock_method_c.return_value = 3
# when the test is done, stop **all** patchers
self.addCleanup(mock.patch.stopall)
def test_one(self):
# use self.mock_method_a, etc.
def test_two(self, mock_method_a, mock_method_b, mock_method_c):
# use self.mock_method_a, etc.
def test_three(self, mock_method_a, mock_method_b, mock_method_c):
# use self.mock_method_a, etc.
Note that the mock.patch.stopall() method will stop all mock patchers that have started. You can also pass the .stop attributes of each of the patchers:
self.addCleanup(patcher_method_a.stop)
self.addCleanup(patcher_method_b.stop)
self.addCleanup(patcher_method_c.stop)
If you have to create a lot of such setups, you could create a helper function that'll take care of the repeated parts:
def setup_object_patch(testcase, object, target, return_value, attrname=None):
patcher = mock.patch.object(object, target)
mock = patcher.start()
mock.return_value = return_value
setattr(testcase, attrname or f'mock_{target}', mock)
testcase.addCleanup(patcher.stop)
and perhaps use this in a loop over a mapping:
def setUp(self):
mocks = {
# attribute name on test -> object, target, return_value
'mock_method_a': (a, 'method_a', 1),
'mock_method_b': (b, 'method_b', 2),
'mock_method_c': (c, 'method_c', 3),
}
for attrname, params in mocks.items():
setup_object_patch(*params, attrname=attrname)
The patcher.start() approach in a TestCase.setUp() method makes it easier to use inheritance, where a base testcase case is used as the basis for several test cases that all use the same shared mocking setup.

mocking an instance method of inner object in python 3.4

I have a class in my code that makes use of a third party library. I would like to mock the instance method of the object that I obtain by calling an instance method of the library class. I am not clear on how to mock the instance method of this inner object. Following is my code:
My class:
from a import A
class MyClass(object):
def __init__(self):
self.obj = A()
def do_something(self):
b = self.obj.get_inner_object()
result = b.do_inner()
return result
Here is my test class:
from unittest import TestCase
from unittest.mock import MagicMock
from unittest.mock import patch
from a import A
class TestMyClass(TestCase):
def __init__(self):
self.my_class = MyClass()
#patch.object(A,'get_inner_object')
def test_do_something(self, mock_get_inner_object):
test_res = self.my_class.do_something()
As you can see above, I would like to mock away 2 methods of my library - get_inner_object() and do_inner() which is the instance method of the object returned by get_inner_object(). I was able to mock get_inner_object(), but I am not clear on how to mock the do_inner() method. Please clarify. Here's the help I am following: https://www.toptal.com/python/an-introduction-to-mocking-in-python
Just mock out all of A:
#patch('a.A')
def test_do_something(self, mock_A):
mock_b = mock_A.return_value.get_inner_object.return_value
mock_b.do_inner.return_value = 'mocked return value'
test_res = self.my_class.do_something()
self.assertEqual(test_res, 'mocked return value')
After all, you are testing MyClass here, not A.
Either way, wether you use #patch.object(A, 'get_inner_object') or patch all of A, the self.obj.get_inner_object() expression calls a Mock instance, so the .return_value attribute is returned at that point. The do_inner method is just another chained call on that returned mock here, so you can set what is returned for that method by setting the .return_value attribute to something you test for.
To translate that back to your #patch.object() situation, mock_b is then the mock_inner_object.return_value object:
#patch.object(A,'get_inner_object')
def test_do_something(self, mock_get_inner_object):
mock_b = mock_get_inner_object.return_value
mock_b.do_inner.return_value = 'mocked return value'
test_res = self.my_class.do_something()
self.assertEqual(test_res, 'mocked return value')

Assert that derived class methods are called in correct order

I'm trying to verify that the implementation of Base.run_this calls the methods of derived class (derived_method_[1st|2nd|3rd]) in correct order. As the output shows, the test is not working. How can I fix this?
class Base(object):
__metaclass__ = abc.ABCMeta
def __init__(self, parameters):
self.parameters = parameters;
#abc.abstractmethod
def must_implement_this(self):
return
def run_this(self):
self.must_implement_this()
if(self.parameters):
first = getattr(self, "derived_method_1st")
first()
second = getattr(self, "derived_method_2nd")
second()
third = getattr(self, "derived_method_3rd")
third()
class Derived(Base):
def must_implement_this(self):
pass
def derived_method_1st(self):
pass
def derived_method_2nd(self):
pass
def derived_method_3rd(self):
pass
mocked = MagicMock(wraps=Derived(True))
mocked.run_this()
mocked.assert_has_calls([call.derived_method_1st(), call.derived_method_2nd(), call.derived_method_3rd()])
Output
AssertionError: Calls not found.
Expected: [call.derived_method_1st(), call.derived_method_2nd(), call.derived_method_3rd()]
Actual: [call.run_this()]
wraps doesn't work well with instances. What happens here is that mocked.run_this returns a new mock object that 'wraps' Derived(True).run_this, where the latter is a bound method to the original Derived() instance.
As such, that method will call self.derived_method_* methods that are bound to that original instance, not to the mock.
You could patch in the run_this method on a spec mock instead:
mock = MagicMock(spec=Derived)
instance = mock()
instance.run_this = Derived.run_this.__get__(instance) # bind to mock instead
instance.parameters = True # adjust as needed for the test
instance.run_this()
Demo:
>>> mock = MagicMock(spec=Derived)
>>> instance = mock()
>>> instance.run_this = Derived.run_this.__get__(instance) # bind to mock instead
>>> instance.parameters = True # adjust as needed for the test
>>> instance.run_this()
>>> instance.mock_calls
[call.must_implement_this(),
call.derived_method_1st(),
call.derived_method_2nd(),
call.derived_method_3rd()]

Using python's mock patch.object to change the return value of a method called within another method

Is it possible to mock a return value of a function called within another function I am trying to test? I would like the mocked method (which will be called in many methods I'm testing) to returned my specified variables each time it is called. For example:
class Foo:
def method_1():
results = uses_some_other_method()
def method_n():
results = uses_some_other_method()
In the unit test, I would like to use mock to change the return value of uses_some_other_method() so that any time it is called in Foo, it will return what I defined in #patch.object(...)
There are two ways you can do this; with patch and with patch.object
Patch assumes that you are not directly importing the object but that it is being used by the object you are testing as in the following
#foo.py
def some_fn():
return 'some_fn'
class Foo(object):
def method_1(self):
return some_fn()
#bar.py
import foo
class Bar(object):
def method_2(self):
tmp = foo.Foo()
return tmp.method_1()
#test_case_1.py
import bar
from mock import patch
#patch('foo.some_fn')
def test_bar(mock_some_fn):
mock_some_fn.return_value = 'test-val-1'
tmp = bar.Bar()
assert tmp.method_2() == 'test-val-1'
mock_some_fn.return_value = 'test-val-2'
assert tmp.method_2() == 'test-val-2'
If you are directly importing the module to be tested, you can use patch.object as follows:
#test_case_2.py
import foo
from mock import patch
#patch.object(foo, 'some_fn')
def test_foo(test_some_fn):
test_some_fn.return_value = 'test-val-1'
tmp = foo.Foo()
assert tmp.method_1() == 'test-val-1'
test_some_fn.return_value = 'test-val-2'
assert tmp.method_1() == 'test-val-2'
In both cases some_fn will be 'un-mocked' after the test function is complete.
Edit:
In order to mock multiple functions, just add more decorators to the function and add arguments to take in the extra parameters
#patch.object(foo, 'some_fn')
#patch.object(foo, 'other_fn')
def test_foo(test_other_fn, test_some_fn):
...
Note that the closer the decorator is to the function definition, the earlier it is in the parameter list.
This can be done with something like this:
# foo.py
class Foo:
def method_1():
results = uses_some_other_method()
# testing.py
from mock import patch
#patch('Foo.uses_some_other_method', return_value="specific_value"):
def test_some_other_method(mock_some_other_method):
foo = Foo()
the_value = foo.method_1()
assert the_value == "specific_value"
Here's a source that you can read: Patching in the wrong place
Let me clarify what you're talking about: you want to test Foo in a testcase, which calls external method uses_some_other_method. Instead of calling the actual method, you want to mock the return value.
class Foo:
def method_1():
results = uses_some_other_method()
def method_n():
results = uses_some_other_method()
Suppose the above code is in foo.py and uses_some_other_method is defined in module bar.py. Here is the unittest:
import unittest
import mock
from foo import Foo
class TestFoo(unittest.TestCase):
def setup(self):
self.foo = Foo()
#mock.patch('foo.uses_some_other_method')
def test_method_1(self, mock_method):
mock_method.return_value = 3
self.foo.method_1(*args, **kwargs)
mock_method.assert_called_with(*args, **kwargs)
If you want to change the return value every time you passed in different arguments, mock provides side_effect.
To add to Silfheed's answer, which was useful, I needed to patch multiple methods of the object in question. I found it more elegant to do it this way:
Given the following function to test, located in module.a_function.to_test.py:
from some_other.module import SomeOtherClass
def add_results():
my_object = SomeOtherClass('some_contextual_parameters')
result_a = my_object.method_a()
result_b = my_object.method_b()
return result_a + result_b
To test this function (or class method, it doesn't matter), one can patch multiple methods of the class SomeOtherClass by using patch.object() in combination with sys.modules:
#patch.object(sys.modules['module.a_function.to_test'], 'SomeOtherClass')
def test__should_add_results(self, mocked_other_class):
mocked_other_class().method_a.return_value = 4
mocked_other_class().method_b.return_value = 7
self.assertEqual(add_results(), 11)
This works no matter the number of methods of SomeOtherClass you need to patch, with independent results.
Also, using the same patching method, an actual instance of SomeOtherClass can be returned if need be:
#patch.object(sys.modules['module.a_function.to_test'], 'SomeOtherClass')
def test__should_add_results(self, mocked_other_class):
other_class_instance = SomeOtherClass('some_controlled_parameters')
mocked_other_class.return_value = other_class_instance
...

Categories

Resources