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.
Related
I have an object MyObject which uses a constant defined in mymodule.constants.MYCONSTANT. I can successfully patch the constant using a context manger like so:
import pytest
from unittest.mock import patch
def test_constant(self):
with patch('mymodule.constants.MYCONSTANT', 3):
MyObject()
However, I can't figure out how to use the equivalent patching using patch as a decorator:
#patch('mymodule.constants.MYCONSTANT', 3)
def test_constant(self, mock_constant):
MyObject()
The above fails with a fixture mock_constant not found error. I tried using
#patch('mymodule.constants.MYCONSTANT', return_value=3)
But MYCONSTANT doesn't get replaced with the value of 3.
This is aligned to the behavior as documented:
unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)
... If patch() is used as a decorator and new is omitted, the created mock is passed in as an extra argument to the decorated function.
So, if you want to pass the extra argument to the decorated function, don't set the new argument:
#patch('mymodule.constants.MYCONSTANT')
def test_constant(self, mock_constant):
...
Setting the new argument means no extra argument would be passed:
#patch('mymodule.constants.MYCONSTANT', 3)
def test_constant(self):
...
Guys the order of parameters matter so first mocks then fixtures, otherwise it will still throw you the same error.
#patch('server.app.UserValidator')
def test_first_mocks(self,mock_user_validator:MagicMock,client_fixture:FlaskClient):
# Arrange
input = {'password':'internal','username':'error'}
mock_user_validator.side_effect = ValueError('testing')
# Act
response = client_fixture.post('/api/users',json=input)
# Assert
mock_user_validator.assert_called_once()
assert response.status_code == 500
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)
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)
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
I have a method that accepts default arguments:
def build_url(endpoint, host=settings.DEFAULT_HOST):
return '{}{}'.format(host, endpoint)
I have a test case that exercises this method:
class BuildUrlTestCase(TestCase):
def test_build_url(self):
""" If host and endpoint are supplied result should be 'host/endpoint' """
result = build_url('/end', 'host')
expected = 'host/end'
self.assertEqual(result,expected)
#patch('myapp.settings')
def test_build_url_with_default(self, mock_settings):
""" If only endpoint is supplied should default to settings"""
mock_settings.DEFAULT_HOST = 'domain'
result = build_url('/end')
expected = 'domain/end'
self.assertEqual(result,expected)
If I drop a debug point in build_url and inspect this attribute settings.DEFAULT_HOST returns the mocked value. However the test continues to fail and the assertion indicates host is assigned the value from my actual settings.py. I know this is because the host keyword argument is set at import time and my mock is not considered.
debugger
(Pdb) settings
<MagicMock name='settings' id='85761744'>
(Pdb) settings.DEFAULT_HOST
'domain'
(Pdb) host
'host-from-settings.com'
Is there a way to override this value at test time so that I can exercise the default path with a mocked settings object?
Functions store their parameter default values in the func_defaults attribute when the function is defined, so you can patch that. Something like
def test_build_url(self):
""" If only endpoint is supplied should default to settings"""
# Use `func_defaults` in Python2.x and `__defaults__` in Python3.x.
with patch.object(build_url, 'func_defaults', ('domain',)):
result = build_url('/end')
expected = 'domain/end'
self.assertEqual(result,expected)
I use patch.object as a context manager rather than a decorator to avoid the unnecessary patch object being passed as an argument to test_build_url.
I applied the other answer to this question, but after the context manager, the patched function was not the same as before.
My patched function looks like this:
def f(foo=True):
pass
In my test, I did this:
with patch.object(f, 'func_defaults', (False,)):
When calling f after (not in) the context manager, the default was completely gone rather than going back to the previous value. Calling f without arguments gave the error TypeError: f() takes exactly 1 argument (0 given)
Instead, I just did this before my test:
f.func_defaults = (False,)
And this after my test:
f.func_defaults = (True,)
An alternate way to do this: Use functools.partial to provide the "default" args you want. This isn't technically the same thing as overriding them; the call-ee sees an explicit arg, but the call-er doesn't have to provide it. That's close enough most of the time, and it does the Right Thing after the context manager exits:
# mymodule.py
def myfunction(arg=17):
return arg
# test_mymodule.py
from functools import partial
from mock import patch
import mymodule
class TestMyModule(TestCase):
def test_myfunc(self):
patched = partial(mymodule.myfunction, arg=23)
with patch('mymodule.myfunction', patched):
self.assertEqual(23, mymodule.myfunction()) # Passes; default overridden
self.assertEqual(17, mymodule.myfunction()) # Also passes; original default restored
I use this for overriding default config file locations when testing. Credit where due, I got the idea from Danilo Bargen here