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')
Related
How do you mock a class property so that accessing the property calls an alternate method or function in Python?
My question is very similar to this one but all of those solutions result in a mocked property that returns a static unchanging value.
I want to do something like:
import unittest
from unittest import mock
from unittest.mock import PropertyMock
from faker import Faker
class MyClass:
#property
def text(self):
return 'blargh'
class Tests(unittest.TestCase):
#mock.patch('__main__.MyClass.text', new_callable=PropertyMock)
def test_mock_error(self, mock_text):
#mock_text.return_value = Faker().text() # outputs the same text
mock_text.return_value = lambda: Faker().text() # outputs a function
print('a:', MyClass().text)
print('b:', MyClass().text)
if __name__ == '__main__':
unittest.main()
and have a: and b: output different text. But currently the mocked property doesn't call the lambda function is mock it to.
I can get it to work if I use a callback proxy like:
#mock.patch('__main__.MyClass.text', new_callable=PropertyMock)
def test_mock_error(self, mock_text):
from proxytypes3 import CallbackProxy
mock_text.return_value = CallbackProxy(lambda: Faker().text())
print('a:', MyClass().text)
print('b:', MyClass().text)
Is there some way to accomplish this using the vanilla mock module?
If you only want to return some canned list of responses, #chepner's answer will work, i.e. assign some iterable to the mock's side_effect. It can also be an infinite iterable as well.
mock = Mock(side_effect=itertools.count())
mock() # => 0
mock() # => 1
mock() # => 2
But if you want it to actually call a function, then just assign that side_effect to a callable.
mock = Mock(side_effect=lambda: datetime.datetime.now())
(mock() - datetime.datetime.now()).total_seconds() < 0.0001 # True
(mock() - datetime.datetime.now()).total_seconds() < 0.0001 # True
And for completion, to do this with a PropertyMock let's adapt this example from documentation
>>> m = MagicMock()
>>> p = PropertyMock(side_effect=itertools.count())
>>> type(m).foo = p
>>> m.foo
0
>>> m.foo
1
For non-Mock classes like your MyClass, you'll want to patch PropertyMock instead of setattr, b/c Mocks are a little special: https://github.com/python/cpython/blob/fa118f0cd32e9b6cba68df10a176b502407243c8/Lib/unittest/mock.py#L405-L407
Putting it all together, running:
import unittest
from unittest.mock import PropertyMock, patch
class MyClass:
#property
def text(self):
return 'blargh'
state = ['a', 'b', 'c']
def stateful_func():
return state.pop()
class Tests(unittest.TestCase):
#patch('__main__.MyClass.text', new=PropertyMock(side_effect=stateful_func))
def test_mock_error(self):
print('a:', MyClass().text)
print('b:', MyClass().text)
if __name__ == '__main__':
unittest.main()
prints:
a: c
b: b
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Use side_effect to provide a list of values to cycle through.
mock_test.side_effect = [Faker().text(), Faker.text()]
Your attempt never calls the function; you explicitly said that MyClass().text should resolve to a function, not the value the function would return.
So I have a file some_class.py with:
class SomeReader:
def read_path(self, url):
return "read_path"
class SomeClass:
def __init__(self, url):
self.reader = SomeReader(url)
print(self.reader.read_path(""))
And a test file some_class_test.py:
from some_class import SomeClass, SomeReader
#patch("some_class.SomeReader")
def test_some_class(mock_some_reader):
def mock_read_path(url):
return "mock_read_path"
mock_some_reader.read_path.side_effect = mock_read_path
SomeClass("")
I'm expecting that when I run this test, it will print mock_read_path but instead it prints <MagicMock name='SomeReader().read_path()' id='140701381288480'>. How do I fix this? I want to mock both the class initialization of SomeReader, hence I use #patch("some_class.SomeReader"). But I also want to mock the read_path function of SomeReader, hence I have mock_some_reader.read_path.side_effect = mock_read_path but that doesn't seem to work.
What you're doing is making a mock of the class itself, not the instances of the classes. The mock replaces call to instantiate SomeReader(url) (basically replacing the __init__ method of the class).
What you want to do is then mock the return value of the fake instance being created by SomeReader(url)
#patch("some_class.SomeReader")
def test_some_class(mock_some_reader):
def mock_read_path(url):
return "mock_read_path"
mock_some_reader.return_value.read_path.side_effect = mock_read_path
SomeClass("")
I want to patch a function(func_to_be_mocked ) returning class object (ReturnValue) with my own mocked version (ReturnValueMock). The function I want to test(func_to_be_tested) is setting some values on the object. But my mocked object is not getting updated and showing the deafult values. (i.e. assert statement in test is failing). What am I missing?
to_test.py
class ReturnValue():
def __init__(self):
self.a = ""
class ToTest():
def func_to_be_mocked(self):
return ReturnValue()
def func_to_be_tested(self):
ret_val = self.func_to_be_mocked()
ret_val.a = "set"
test.py
from mock import patch
import unittest
from .to_test import ToTest
class ReturnValueMock():
def __init__(self):
self.a = ""
class Tests(unittest.TestCase):
def setUp(self):
self.ret_val = ReturnValueMock()
#patch("to_test.ToTest.func_to_be_mocked")
def test(self, mocked_func):
mocked_func.return_val = self.ret_val
ToTest().func_to_be_tested()
assert self.ret_val == "set"
Thing tried so far
Added print statements to verify that the object in function under test is same as the one I am providing as mock (Object hash code code are same)
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()]
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
...