python mock global function that is used in class - python

I can't seem to get my head around mocking in Python. I have a global function:
a.py:
def has_permission(args):
ret_val = ...get-true-or-false...
return ret_val
b.py:
class MySerializer(HyperlinkedModelSerializer):
def get_fields():
fields = super().get_fields()
for f in :
if has_permission(...):
ret_val[f.name] = fields[f]
return ret_val
c.py:
class CountrySerializer(MySerializer):
class Meta:
model = Country
Question: Now i want to test c.py, but i want to mock the has_permission function that is defined in a.py, but is called in the get_fields-method of the class MySerializer that is defined in b.py ... How do i do that?
I've tried things like:
#patch('b.MySerializer.has_permission')
and
#patch('b.MySerializer.get_fields.has_permission')
and
#patch('a.has_permission')
But everything i try either just doesn't work and has_permission is still executed, or python complains about that it can't find the attribute 'has_permission'
with the patching done in:
test.py
class TestSerializerFields(TestCase):
#patch(... the above examples....)
def test_my_country_serializer():
s = CountrySerializer()
self..assertTrue(issubclass(my_serializer_fields.MyCharField, type(s.get_fields()['field1'])))

You need to patch the global in the b module:
#patch('b.has_permission')
because that's where your code looks for it.
Also see the Where to patch section of the mock documentation.

You need to patch the method where it exists at the time your test runs. If you try and patch the method where it is defined after the test code has already imported it, then the patch will have no effect. At the point where the #patch(...) executes, the test code under test has already grabbed the global method into its own module.
Here is an example:
app/util/config.py:
# This is the global method we want to mock
def is_search_enabled():
return True
app/service/searcher.py:
# Here is where that global method will be imported
# when this file is first imported
from app.util.config import is_search_enabled
class Searcher:
def __init__(self, api_service):
self._api_service = api_service
def search(self):
if not is_search_enabled():
return None
return self._api_service.perform_request('/search')
test/service/test_searcher.py:
from unittest.mock import patch, Mock
# The next line will cause the imports of `searcher.py` to execute...
from app.service.searcher import Searcher
# At this point, searcher.py has imported is_search_enabled into its module.
# If you later try and patch the method at its definition
# (app.util.config.is_search_enabled), it will have no effect because
# searcher.py won't look there again.
class MockApiService:
pass
class TestSearcher:
# By the time this executes, `is_search_enabled` has already been
# imported into `app.service.searcher`. So that is where we must
# patch it.
#patch('app.service.searcher.is_search_enabled')
def test_no_search_when_disabled(self, mock_is_search_enabled):
mock_is_search_enabled.return_value = False
mock_api_service = MockApiService()
mock_api_service.perform_request = Mock()
searcher = Searcher(mock_api_service)
results = searcher.search()
assert results is None
mock_api_service.perform_request.assert_not_called()
# (For completeness' sake, make sure the code actually works when search is enabled...)
def test_search(self):
mock_api_service = MockApiService()
mock_api_service.perform_request = mock_perform_request = Mock()
searcher = Searcher(mock_api_service)
expected_results = [1, 2, 3]
mock_perform_request.return_value = expected_results
actual_results = searcher.search()
assert actual_results == expected_results
mock_api_service.perform_request.assert_called_once_with('/search')

Related

mocking a class instance in pytest

am trying to mock a class instance while testing a method something like below
source
main_proc.py
devinstance.py
prodinstance.py
requirements.txt
host.json
main_proc.py
def get_instance(self)
ins = None
env = os.getenv('env', 'dev')
if env == 'dev':
ins = DevInstance()
else:
ins = ProdInstance()
return ins
Sample DevInstance class
devinstance.py
class DevInstance:
def __init__(self):
self.eh_client = dict()
self.initialize()
def initialize(self):
try:
client = EventHubProducerClient.from_connection_string(conn_str=self.secrets_dict[value],
eventhub_name=names[i], http_proxy=HTTP_PROXY)
except Exception as e:
logging.error(e)
raise e
testing the get instance like below as my intension is to mock the entire DevInstance class obj. both files are in the same module.
#mock.patch("devinstance.DevInstance")
def test_get_instance(self, devins):
# Act
devins.return_value = MagicMock()
result = get_instance()
# Assert
assert result is not None
Can anyone help me how this can be acheived?
You need to patch where the object is being looked up (see the Where to patch documentation).
If you're testing get_instance in main_proc, then you need to patch where DevInstance is imported in main_proc.
For example, if you're importing it with from devinstance import DevInstance, then you need to patch it with #mock.patch("main_proc.DevInstance").
Otherwise, if you're importing it with import devinstance, then you need to patch it with #mock.patch("main_proc.devinstance.DevInstance").

mocking external library return value throws access violation exception

I'm writing TC for my method using external library sklearn.neighbors.KDTree.
My test target method is below,
# target.py
from sklearn.neighbors import KDTree
#staticmethod
def mymethod(a, b):
...
dist, index = KDTree(a).query(b, k=3)
# manipulate the return value from KDTree.query
...
and, the code I tried as TC is this.
# mytest.py
from unittest import mock
#mock.patch('sklearn.neighbors.KDTree')
def test_mymethod(mock_kdtree):
# make test data and set mock
a = ...
b = ...
mock_kdtree.return_value.query.return_value = ...
# execute test target
mymethod(a, b)
assert mock_kdtree.called
When running TC it throws exception, Windows fatal exception: access violation on the line calling dist, index = KDTree(a).query(b, k=3).
Is there something wrong to mock KDTree return value?
It is not mocking the method correctly.
You typically skip the step where you set the mock instance of the class.
from unittest import mock
import sklearn
#patch('sklearn.neighbors.KDTree')
def test_mymethod(mock_kdtree):
# make test data and set mock
a = ...
b = ...
# First create a mock instance of the kdtree class
mock_kdtree_instance = mock.MagicMock()
mock_kdtree_instance.query.return_value = ...
# assign this instance to the class mock
mock_kdtree.return_value = mock_kdtree_instance
# execute test target
mymethod(a, b)
mock_kdtree_query.assert_called()
For someone who might be struggling with same problem, I share how I made my test.
The key is using monkeypatch, from the advice #jossefaz. Thank you!
# mytest.py
def test_mymethod(monkeypatch):
class MockKDTree(object):
def __init__(self, *fake_args):
pass
def query(self, *fake_args, **fake_kwargs):
return fake_kdtree_dists, None # This is what I wanted to return. You need to prepare.
monkeypatch.setattr("mypackage.mymodule.KDTree", MockKDTree)
# execute test target
mymethod(a, b)
# assertion

python unittest : Assert the sequence of method calls in unit test [duplicate]

I have three functions that I'm trying to test the call order of.
Let's say that in module module.py I have the following
# module.py
def a(*args):
# do the first thing
def b(*args):
# do a second thing
def c(*args):
# do a third thing
def main_routine():
a_args = ('a')
b_args = ('b')
c_args = ('c')
a(*a_args)
b(*b_args)
c(*c_args)
I want to check that b is called after a, and before c. So getting a mock for each of a, b and c is easy:
# tests.py
#mock.patch('module.a')
#mock.patch('module.b')
#mock.patch('module.c')
def test_main_routine(c_mock, b_mock, a_mock):
# test all the things here
Checking that each of the individial mocks are called is easy, too. How do I check the order of the calls relative to one another?
call_args_list won't work as it's maintained separately for each mock.
I've tried using a side effect to log each of the calls:
calls = []
def register_call(*args):
calls.append(mock.call(*args))
return mock.DEFAULT
a_mock.side_effect = register_call
b_mock.side_effect = register_call
c_mock.side_effect = register_call
But this only gives me the args that the mocks were called with, but not the actual mock that the call was made against. I can add a bit more logic:
# tests.py
from functools import partial
def register_call(*args, **kwargs):
calls.append(kwargs.pop('caller', None), mock.call(*args, **kwargs))
return mock.DEFAULT
a_mock.side_effect = partial(register_call, caller='a')
b_mock.side_effect = partial(register_call, caller='b')
c_mock.side_effect = partial(register_call, caller='c')
And that seems to get the job done... Is there a better way though? It feels like there should already be something in the API that can do this that I'm missing.
Define a Mock manager and attach mocks to it via attach_mock(). Then check for the mock_calls:
#patch('module.a')
#patch('module.b')
#patch('module.c')
def test_main_routine(c, b, a):
manager = Mock()
manager.attach_mock(a, 'a')
manager.attach_mock(b, 'b')
manager.attach_mock(c, 'c')
module.main_routine()
expected_calls = [call.a('a'), call.b('b'), call.c('c')]
assert manager.mock_calls == expected_calls
Just to test that it works, change the order of function calls in the main_routine() function add see that it throws AssertionError.
See more examples at Tracking order of calls and less verbose call assertions (link is dead; substitute: https://docs.python.org/3/library/unittest.mock.html#attaching-mocks-as-attributes)
Hope that helps.
I needed this answer today, but the example code in the question is really hard to read because the call args are the same as the names of the mocks on the manager and in the scope of the test. Here's the official documentation on this concept, and below is a clearer example for non-robots. All the modules I'm patching are made-up for the sake of the example:
#patch('module.file_reader')
#patch('module.json_parser')
#patch('module.calculator')
def test_main_routine(mock_calculator, mock_json_parser, mock_file_reader):
manager = Mock()
# First argument is the mock to attach to the manager.
# Second is the name for the field on the manager that holds the mock.
manager.attach_mock(mock_file_reader, 'the_mock_file_reader')
manager.attach_mock(mock_json_parser, 'the_mock_json_parser')
manager.attach_mock(mock_calculator, 'the_mock_calculator')
module.main_routine()
expected_calls = [
call.the_mock_file_reader('some file'),
call.the_mock_json_parser('some json'),
call.the_mock_calculator(1, 2)
]
assert manager.mock_calls == expected_calls
Note that you have to use attach_mock in this case because your mocks were created by patch. Mocks with names, including those created by patch, must be attached via attach_mock for this code to work. You don't have to use attach_mock if you make your own Mock objects without names:
def test_main_routine(mock_calculator, mock_json_parser, mock_file_reader):
manager = Mock()
mock_file_reader = Mock()
mock_json_parser = Mock()
mock_calculator = Mock()
manager.the_mock_file_reader = mock_file_reader
manager.the_mock_json_parser = mock_json_parser
manager.the_mock_calculator = mock_calculator
module.main_routine()
expected_calls = [
call.the_mock_file_reader('some file'),
call.the_mock_json_parser('some json'),
call.the_mock_calculator(1, 2)
]
assert manager.mock_calls == expected_calls
If you want a clear assertion failed message when the order or expected calls are missing, use the following assert line instead.
self.assertListEqual(manager.mock_calls, [
call.the_mock_file_reader('some file'),
call.the_mock_json_parser('some json'),
call.the_mock_calculator(1, 2)
])
A cleaner solution would be to wrap your functions into a class, then mock the class in the test. This will eliminate the need to do any patching (always a plus).
# module.py
class Wrapper:
def a(self, *args):
pass
def b(self, *args):
pass
def c(self, *args):
pass
def main_routine(self):
a_args = ('arg for a',)
b_args = ('arg for b',)
c_args = ('arg for c',)
self.a(*a_args)
self.b(*b_args)
self.c(*c_args)
In the test file, you create a mock wrapper class, then insert the mock wrapper in as the argument self when calling Wrapper.main_method (notice that this does not instantiate the class).
# module_test.py
from unittest.mock import MagicMock, call
from module import Wrapper
def test_main_routine():
mock_wrapper = MagicMock()
Wrapper.main_routine(mock_wrapper)
expected_calls = [call.a('arg for a'),
call.b('arg for b'),
call.c('arg for c')]
mock_wrapper.assert_has_calls(expected_calls)
Benefits:
No patching needed
In the test, you only need to type the name of the method being called once (instead of 2-3 times)
Uses assert_has_calls instead of comparing the mock_calls attribute to a list of calls.
Can be made into a general check_for_calls function (see below)
# module_better_test.py
from unittest.mock import MagicMock, call
from module import Wrapper
def test_main_routine():
expected_calls = [call.a('arg for a'),
call.b('arg for b'),
call.c('arg for c')]
check_for_calls('main_routine', expected_calls)
def check_for_calls(method, expected_calls):
mock_wrapper = MagicMock()
getattr(Wrapper, method)(mock_wrapper)
mock_wrapper.assert_has_calls(expected_calls)

How to mock a function with specific parameter and return value that calls another function in python unit test?

i am new to python unit testing. Want to mock a function that calls other functions.
Here is my function that i want to mock
def has_groups(self, group_names):
auth_user_id = AuthUser.get_by_email(self.userEmail).id
auth_user_groups = AuthUserGroups.get_group_by_user_id(auth_user_id)
for auth_user_group in auth_user_groups:
if auth_user_group.group.name in group_names:
return True
return False
has_groups should return True only when it get's 'Admin' as parameter.
Here is my test
def my_test(self):
uid = self.auth_user.get_by_email = Mock(return_value=73)
groups = AuthUserGroups.get_group_by_user_id = Mock(uid, return_value='Admin')
self.auth_user.has_groups = Mock(groups, return_value=True)
but it's not working fine. I will appreciate if anyone help me
Can i use patch decorator for this and how?
As I understand your has_groups function is method. I think it's better to mock whole class or independent function. On this situation you could mock AuthUserGroups, method return value and patch module with has_groups method implementation. So you'll have test like this:
from unittest import mock
def my_test(self):
group = mock.MagicMock()
group.group.name = 'Admin'
fake_auth_user_groups = mock.MagicMock()
fake_auth_user_groups.get_group_by_user_id.return_value = [group]
with mock.patch('your_module.AuthUserGroups', fake_auth_user_groups):
self.auth_user.has_groups(['Admin'])

Checking call order across multiple mocks

I have three functions that I'm trying to test the call order of.
Let's say that in module module.py I have the following
# module.py
def a(*args):
# do the first thing
def b(*args):
# do a second thing
def c(*args):
# do a third thing
def main_routine():
a_args = ('a')
b_args = ('b')
c_args = ('c')
a(*a_args)
b(*b_args)
c(*c_args)
I want to check that b is called after a, and before c. So getting a mock for each of a, b and c is easy:
# tests.py
#mock.patch('module.a')
#mock.patch('module.b')
#mock.patch('module.c')
def test_main_routine(c_mock, b_mock, a_mock):
# test all the things here
Checking that each of the individial mocks are called is easy, too. How do I check the order of the calls relative to one another?
call_args_list won't work as it's maintained separately for each mock.
I've tried using a side effect to log each of the calls:
calls = []
def register_call(*args):
calls.append(mock.call(*args))
return mock.DEFAULT
a_mock.side_effect = register_call
b_mock.side_effect = register_call
c_mock.side_effect = register_call
But this only gives me the args that the mocks were called with, but not the actual mock that the call was made against. I can add a bit more logic:
# tests.py
from functools import partial
def register_call(*args, **kwargs):
calls.append(kwargs.pop('caller', None), mock.call(*args, **kwargs))
return mock.DEFAULT
a_mock.side_effect = partial(register_call, caller='a')
b_mock.side_effect = partial(register_call, caller='b')
c_mock.side_effect = partial(register_call, caller='c')
And that seems to get the job done... Is there a better way though? It feels like there should already be something in the API that can do this that I'm missing.
Define a Mock manager and attach mocks to it via attach_mock(). Then check for the mock_calls:
#patch('module.a')
#patch('module.b')
#patch('module.c')
def test_main_routine(c, b, a):
manager = Mock()
manager.attach_mock(a, 'a')
manager.attach_mock(b, 'b')
manager.attach_mock(c, 'c')
module.main_routine()
expected_calls = [call.a('a'), call.b('b'), call.c('c')]
assert manager.mock_calls == expected_calls
Just to test that it works, change the order of function calls in the main_routine() function add see that it throws AssertionError.
See more examples at Tracking order of calls and less verbose call assertions (link is dead; substitute: https://docs.python.org/3/library/unittest.mock.html#attaching-mocks-as-attributes)
Hope that helps.
I needed this answer today, but the example code in the question is really hard to read because the call args are the same as the names of the mocks on the manager and in the scope of the test. Here's the official documentation on this concept, and below is a clearer example for non-robots. All the modules I'm patching are made-up for the sake of the example:
#patch('module.file_reader')
#patch('module.json_parser')
#patch('module.calculator')
def test_main_routine(mock_calculator, mock_json_parser, mock_file_reader):
manager = Mock()
# First argument is the mock to attach to the manager.
# Second is the name for the field on the manager that holds the mock.
manager.attach_mock(mock_file_reader, 'the_mock_file_reader')
manager.attach_mock(mock_json_parser, 'the_mock_json_parser')
manager.attach_mock(mock_calculator, 'the_mock_calculator')
module.main_routine()
expected_calls = [
call.the_mock_file_reader('some file'),
call.the_mock_json_parser('some json'),
call.the_mock_calculator(1, 2)
]
assert manager.mock_calls == expected_calls
Note that you have to use attach_mock in this case because your mocks were created by patch. Mocks with names, including those created by patch, must be attached via attach_mock for this code to work. You don't have to use attach_mock if you make your own Mock objects without names:
def test_main_routine(mock_calculator, mock_json_parser, mock_file_reader):
manager = Mock()
mock_file_reader = Mock()
mock_json_parser = Mock()
mock_calculator = Mock()
manager.the_mock_file_reader = mock_file_reader
manager.the_mock_json_parser = mock_json_parser
manager.the_mock_calculator = mock_calculator
module.main_routine()
expected_calls = [
call.the_mock_file_reader('some file'),
call.the_mock_json_parser('some json'),
call.the_mock_calculator(1, 2)
]
assert manager.mock_calls == expected_calls
If you want a clear assertion failed message when the order or expected calls are missing, use the following assert line instead.
self.assertListEqual(manager.mock_calls, [
call.the_mock_file_reader('some file'),
call.the_mock_json_parser('some json'),
call.the_mock_calculator(1, 2)
])
A cleaner solution would be to wrap your functions into a class, then mock the class in the test. This will eliminate the need to do any patching (always a plus).
# module.py
class Wrapper:
def a(self, *args):
pass
def b(self, *args):
pass
def c(self, *args):
pass
def main_routine(self):
a_args = ('arg for a',)
b_args = ('arg for b',)
c_args = ('arg for c',)
self.a(*a_args)
self.b(*b_args)
self.c(*c_args)
In the test file, you create a mock wrapper class, then insert the mock wrapper in as the argument self when calling Wrapper.main_method (notice that this does not instantiate the class).
# module_test.py
from unittest.mock import MagicMock, call
from module import Wrapper
def test_main_routine():
mock_wrapper = MagicMock()
Wrapper.main_routine(mock_wrapper)
expected_calls = [call.a('arg for a'),
call.b('arg for b'),
call.c('arg for c')]
mock_wrapper.assert_has_calls(expected_calls)
Benefits:
No patching needed
In the test, you only need to type the name of the method being called once (instead of 2-3 times)
Uses assert_has_calls instead of comparing the mock_calls attribute to a list of calls.
Can be made into a general check_for_calls function (see below)
# module_better_test.py
from unittest.mock import MagicMock, call
from module import Wrapper
def test_main_routine():
expected_calls = [call.a('arg for a'),
call.b('arg for b'),
call.c('arg for c')]
check_for_calls('main_routine', expected_calls)
def check_for_calls(method, expected_calls):
mock_wrapper = MagicMock()
getattr(Wrapper, method)(mock_wrapper)
mock_wrapper.assert_has_calls(expected_calls)

Categories

Resources