Conditional mocking: Call original function if condition does match - python

How can I conditionally call the original method in a mock?
In this example I only want to fake a return value if bar=='x'. Otherwise I want to call the original method.
def mocked_some_method(bar):
if bar=='x':
return 'fake'
return some_how_call_original_method(bar)
with mock.patch('mylib.foo.some_method', mocked_some_method):
do_some_stuff()
I know that it is a bit strange. If I want to fake mylib.foo.some_method in side do_some_stuff() it should be condition-less. All (not some) calls to some_method should be mocked.
In my case it is an integration test, not a s tiny unittest and mylib.foo.some_method is a kind of dispatcher which gets used very often. And in one case I need to fake the result.
Update
I wrote this question four years ago. Today, it feels very strange to do conditional mocking. Mocks should only get used in tests. Tests (and production code) should be simple and small. Tests should be conditionless. As I wrote this question, we still used huge production methods and long test. Today, I follow these rules (simple methods, conditionless tests ...). I wrote my findings down: my programming guidelines

If you need just replace behavior without care of mock's calls assert function you can use new argument; otherwise you can use side_effect that take a callable.
I guess that some_method is a object method (instead of a staticmethod) so you need a reference its object to call it. Your wrapper should declare as first argument the object and your patch use autospec=True to use the correct signature for side_effect case.
Final trick is save the original method reference and use it to make the call.
orig = mylib.foo.some_method
def mocked_some_method(self, bar):
if bar=='x':
return 'fake'
return orig(self, bar)
#Just replace:
with mock.patch('mylib.foo.some_method', new=mocked_some_method):
do_some_stuff()
#Replace by mock
with mock.patch('mylib.foo.some_method', side_effect=mocked_some_method, autospec=True) as mock_some_method:
do_some_stuff()
assert mock_some_method.called

Returning unittest.mock.DEFAULT using return_value or side_effects will call original method. This should allow to avoid some of the wrappers.

Here is an example of how you can dynamically patch class method and execute the original method when you need
Code which needs to be tested
class CheckMockMethod:
def get_value_x_10(self, value):
"""Method which is going to be patched"""
return value*10
def wrapper_func():
"""Function which is called from test"""
for i in [1, 2, 3]:
print(CheckMockMethod().get_value_x_10(i))
ContextManager test helper for mock.patcher
import mock
from contextlib import contextmanager
#contextmanager
def mock_patch_method_original(mock_path, original_method, results):
results = iter(results)
def side_effect(self, *args, **kwargs):
value = next(results)
if value == '__original__':
side_effect.self = self
return original_method(self, *args, **kwargs)
else:
return value
patcher = mock.patch(mock_path, autospec=True, side_effect=side_effect)
yield patcher.start()
patcher.stop()
Example test method, see the value of results var, __original__ when you want to return original value
from somewhere import CheckMockMethod
from test_helpers import mock_patch_method_original
def test_wrapper(self):
path = '<import-path>.CheckMockMethod.get_value_x_10'
orig = CheckMockMethod.get_value_x_10
results = [1000, '__original__', 3000]
with mock_patch_method_original(path, original_method=orig, results=results):
wrapper_func()
Results
With mock you will see
1000, 20, 3000
where 1000 and 3000 are patched values and 20 is original

Related

Is it possible to check the call parameters in the test?

is it possible to check in the test with what parameters the method is called and what result it returns, if we call the main method run which calls the method I'm interested in - self.get_request().
file.py
class A:
def run():
some logic...
request = self.get_request()
some logic...
return response
test.py
from file.py import A
def test():
"""
Inside this test, I want to check the parameters and the value returned by the
get_request method, but I don't want to check it separately
I want to check it by calling the parent method - run
"""
instance = A()
response = instance.run()
assertions logic for instance.get_request..
I know that it is possible to mock a method and then we have access to the number of calls, parameters, etc. If what I'm asking is possible in some way through mock, I just want to add that my mock would have to have the same logic as the method it mocks (be the same).
What you are asking for is probably the wraps argument that can be used in patch - this allows you to mock a function, while it still retains the previous (or some other) functionality (note that the argument itself is described under Mock). As with any mock, this does allow you to test the calls and call args, but does not allow you to check the return value of the function. This has to be tested via its side effects (in your case via the returned response which should depend on the return value of get_request).
Here is an illustration for your case:
from unittest import mock
class A:
def run(self):
request = self.get_request(21)
return request
def get_request(self, foo):
return foo * 2
def test_run():
instance = A()
with mock.patch.object(instance, "get_request", wraps=instance.get_request) as mocked:
assert instance.run() == 42
mocked.assert_called_once_with(21)
In this case the mock calls the real get_request method and returns its result, while recording the call and the call args.
I added some argument to get_request for demonstration, and returned the result of the call directly in run - in your case this will differ of course, but the idea should be the same.

Mock a subset of a Python class's methods and properties

I'm using the mock Python module for performing my tests.
There are times when I'm mocking a class, however I just want to mock some of its methods and properties, and not all of them.
Suppose the following scenario:
# module.py
class SomeClass:
def some_method(self):
return 100
def another_method(self):
return 500
# test.py
class Tests(unittest.TestCase):
#patch('module.SomeClass')
def test_some_operation(self, some_class_mock):
some_class_instance = some_class_mock.return_value
# I'm mocking only the some_method method.
some_class_instance.some_method.return_value = 25
# This is ok, the specific method I mocked returns the value I wished.
self.assertEquals(
25,
SomeClass().some_method()
)
# However, another_method, which I didn't mock, returns a MagicMock instance
# instead of the original value 500
self.assertEquals(
500,
SomeClass().another_method()
)
On the code above, once I patch the SomeClass class, calls to methods whose return_values
I didn't exlicitely set will return MagicMock objects.
My question is: How can I mock only some of a class methods but keep others intact?
There are two ways I can think of, but none of them are really good.
One way is to set the mock's method to the original class method, like this:
some_class_instance.another_method = SomeClass.another_method
This is not really desirable because the class may have a lot of methods and properties to
"unmock".
Another way is to patch each method I want explicitly, such as:
#patch('module.SomeClass.some_method')
def test_some_operation(self, some_method_mock):
But this doesn't really work if I want to mock the class itself, for mocking calls to the
initializer for example. The code below would override all SomeClass's methods anyway.
#patch('module.SomeClass.some_method')
#patch('module.SomeClass')
def test_some_operation(self, some_class_mock, some_method_mock):
Here is a more specific example:
class Order:
def process_event(self, event, data):
if event == 'event_a':
return self.process_event_a(data)
elif event == 'event_b':
return self.process_event_b(data)
else:
return None
def process_event_a(self, data):
# do something with data
def process_event_b(self, data):
# do something different with data
In this case, I have a general method process_event which calls a specific processing event depending on the supplied event.
I would like to test only the method process_event. I just want to know if the proper specific event is called depending on the event I supply.
So, in my test case what I want to do is to mock just process_event_a and process_event_b, call the original process_event with specific parameters, and then assert either process_event_a or process_event_b were called with the proper parameters.
Instead of patching the whole class, you must patch the object. Namely, make an instance of your class, then, patch the methods of that instance.
Note that you can also use the decorator #patch.object instead of my approach.
class SomeClass:
def some_method(self):
return 100
def another_method(self):
return 500
In your test.py
from unittest import mock
class Tests(unittest.TestCase):
def test_some_operation(self):
some_class_instance = SomeClass()
# I'm mocking only the some_method method.
with mock.patch.object(some_class_instance, 'some_method', return_value=25) as cm:
# This is gonna be ok
self.assertEquals(
25,
SomeClass().some_method()
)
# The other methods work as they were supposed to.
self.assertEquals(
500,
SomeClass().another_method()
)

Pytest mock an object returned via yield and check whether a method of that object is called

I have a piece of code need to be tested via pytest
def my_function(value):
with some_generator() as gen:
gen.some_method(value)
I need to check whether the some_method has been called or not. I have used pytest-mock to mock the some_generator method to return a MagicMock and used that object to check if the method is called. But it is returning false. Also the return value I assigned is not reflected to the some_method
def test_myfunction(mocker):
generator = mocker.patch('some_generator')
mocked_obj = mock.MagicMock()
generator.return_value = mocked_obj
my_function(1)
assert mocked_obj.some_method.called
The test is always failing even though the gen.some_method(value) is called.
I think you have two problems:
You're not telling mocker where some_generator is. I think you need to include a module name.
You're mocking the return value of the generator, but not the __enter__() call that the with statement makes. You don't need a separate mock object for that, mocker already creates a mock object for return_value and any nested attributes.
Here's a fixed version of your test:
from scratch import my_function
def test_myfunction(mocker):
generator = mocker.patch('scratch.some_generator')
my_function(1)
assert generator.return_value.__enter__.return_value.some_method.called
Just for completeness, here's the runnable version of my_function that I used:
# scratch.py
def some_generator():
pass
def my_function(value):
with some_generator() as gen:
gen.some_method(value)

Change decorator to implement in new module

I have some class with a field spent_times. spent_times is a list and all methods of this class write some information, which is valuable for logging.
Also, I have a decorator, which calculate execution time for every function and write it to spent_times.
This is realization of my decorator:
def timing(message):
def wrap(function):
def called(*args, **kwargs):
time_start = timer()
spent_time = round(timer() - time_start, 5)
if not args:
return function(*args, **kwargs), spent_time
obj = args[0]
if hasattr(obj, "spent_times"):
obj.spent_times.append("{}={:.5f}".format(message, spent_time))
return function(*args, **kwargs)
else:
logging.warning('Decorator allows to set spent_time attribute!')
return called
return wrap
As you can see in my decorator there is a check, if the calling function has attribute self.
If it has, than I can write needed info in list spent_times on the spot, if it does not have, than decorator returns time spent on execution and function itself.
I am using this decorator in one single module and second case (when no self found) belongs to some other functions in this module, which does not belong to class, where spent_time list is defined, but I execute them inside my class, so I am able to realize for example the following structure:
This is declaration of "outer" function
def calc_users(requests, priority):
# .....
And inside my class I execute it and update my spent_time list this way:
response, spent_time = calc_users(requests, priority)
self.class_obj.spent_times.append("user_calculation={:.5f}".format(spent_time))
which is not very nice, but it is working at least.
Now, I moved a few functions of my class in different new module and I would like to use the same decorator timing.
Can someone help me to implement this realization of timing in new module. I do not know, what can I do to update my spent_times list now.
These two modules will work at the same time and I cannot create object of class and pass it as an argument to new module, because (as far as I understand it) there will be two objects and spent_times will not be updated correctly.
Maybe there is a way to pass a reference to spent_times somehow, but I do not want to change arguments of my functions in new module, since I think in this case principle of shared responsibility will be broken (decorator is responsible for logging, function for its action).
So how to improve decorator or how to pass spent_times list to a new module?
Any help will be greatly appreciate!
P.S.
Maybe make spent_times a global variable? (in the very worst case)
A global list seems fine but you can also use a class and create a singleton by deleting the class after instantiation. This prevents from creating another another instance:
# mymodule.py
from timeit import default_timer as timer
class Timing(object):
def __init__(self):
self.spent_times = []
def __call__(self, message):
def wrap(function):
def called(*args, **kwargs):
time_start = timer()
spent_time = round(timer() - time_start, 5)
self.spent_times.append("{}={:.5f}".format(message, spent_time))
return function(*args, **kwargs)
return called
return wrap
timing = Timing()
del Timing # prevent another instance
Now import in another module:
from mymodule import timing
#timing('Hello')
def add(a, b):
return a + b
The special method __call__ makes an instance of a class behave like a function, i.e. it is callable with ().
The advantage it that you can use self.attr instead of a global variable.
The deletion of the class after instantiation prevents from creating another instance. This is called a singleton. Now all your timings end up in the same list no matter how often you use timing as a decorator.

Python3 mock: assert_has_calls for production-code methods?

I've got this production class:
class MyClass:
def __init__(self):
self.value = None
def set_value(self, value):
self.value = value
def foo(self):
# work with self.value here
# raise RuntimeError("error!")
return "a"
Which is being used from another place, like this:
class Caller:
def bar(self, smth):
obj = MyClass()
obj.set_value(smth)
# ...
# try:
obj.foo()
# except MyError:
# pass
obj.set_value("str2")
# obj.foo()
and I got this:
class MyError(Exception):
pass
In my test I want to make sure that Caller.bar calls obj.set_value, first with smth="a", then with smth="b", but I want it to really set the value (i.e. call the real set_value method). Is there any way for me to tell the mock to use the actual method, so I can later on read what it was called with?
P.S. I know that I can just change "foo" to require the parameter "smth" so I could get rid of "set_value", but I want to know if there is another option than this.
Okay, so I have tried this in my test:
def test_caller(self):
with patch('fullpath.to.MyClass', autospec=MyClass) as mock:
mock.foo.side_effect = [MyError("msg"), "text"]
caller = Caller()
caller.bar("str1")
calls = [call("str1"), call("str2")]
mock.set_value.assert_has_calls(calls)
But I see that the mock was not successful since the real "foo" is called when I wanted it to first raise MyError, then return "text".
Also, the assertion fails:
AssertionError: Calls not found.
Expected: [call('str1'), call('str2')]
Actual: []
The problem here is that you have mocked out your Class, and are not properly using the instance of your class. This is why things are not behaving as expected.
So, lets take a look at what is going on.
Right here:
with patch('fullpath.to.MyClass', autospec=MyClass) as mock:
So, what you are doing right here is mocking out your class MyClass only. So, when you are doing this:
mock.set_value.assert_has_calls(calls)
And inspect what is going on when you execute your unittest, your mock calls will actually contain this:
[call().set_value('str1'), call().foo(), call().set_value('str2')]
Pay attention to call as it is written as call(). call is with reference to your mock here. So, with that in mind, you need to use the called (aka return_value within context of the mocking world) mock to properly reference your mock object that you are trying to test with. The quick way to fix this is simply use mock(). So you would just need to change to this:
mock().set_value.assert_has_calls(calls)
However, to be more explicit on what you are doing, you can state that you are actually using the result of calling mock. Furthermore, it would actually be good to note to use a more explicit name, other than mock. Try MyClassMock, which in turn you name your instance my_class_mock_obj:
my_class_mock_obj = MyClassMock.return_value
So in your unit test it is more explicit that you are using a mocked object of your class. Also, it is always best to set up all your mocking before you make your method call, and for your foo.side_effect ensure that you are also using the instance mock object. Based on your recent update with your exception handling, keep your try/except without comments. Putting this all together, you have:
def test_caller(self):
with patch('tests.test_dummy.MyClass', autospec=MyClass) as MyClassMock:
my_class_mock_obj = MyClassMock.return_value
my_class_mock_obj.foo.side_effect = [MyError("msg"), "text"]
caller = Caller()
caller.bar("str1")
calls = [call("str1"), call("str2")]
my_class_mock_obj.set_value.assert_has_calls(calls)

Categories

Resources