Is it possible to patch method called on created previously mock? - python

I have fixture which create mocker.Mock instead of gui.Menu object during initialization of gui.Buttons. Reference is stored in Buttons.menu attribute. In my test I check if proper function is called in gui.Buttons.add. Parametrized test worked well until I met condition where method from gui.Menu should be called. Now there is a mock.
import pytest
from project import gui
#pytest.fixture
def buttons(mocker):
mocker.patch('project.gui.tk.Frame.__init__', return_value=None)
mocker.patch('project.gui.tk.Button')
return gui.Buttons(mocker.Mock())
#pytest.mark.parametrize('value,patched',(
(None, 'project.gui.show_no_connection'),
(False, 'project.gui.Buttons.process_data'),
(True, 'pytest.Mock.show_error'),
))
def test_add_calls_function(buttons, value, patched, mocker):
mocker.patch('project.gui.Buttons.exist_check', return_value=value)
mocked = mocker.patch(patched)
buttons.add()
mocked.assert_called_once()
With real object I could write (True, 'project.gui.Menu.show_error') inside #pytest.mark.parametrize instead of (True, 'pytest.Mock.show_error') which doesn't work and produces ModuleNotFoundError: No module named 'pytest.Mock'.
I wonder if it is possible to patch created mock object in my fixture to make it work like other parametrized examples. Is it even possible? Correct me please if way how I understand it is wrong.
Tested code looks like:
import tkinter as tk
import tkinter.messagebox as msg
from project.connection import Database
def show_no_connection():
msg.showerror('Error', 'Could not perform operation. Try again later.')
class Menu(tk.Tk):
def __init__(self):
super().__init__()
self.form = Form()
def show_error(self, message):
self.form.clear()
msg.showerror('Error', message)
class Form(tk.Frame):
def clear(self):
print('Clearing...')
def get(self):
return {'Title': 'Test', 'ISBN': 87327837823}
class Buttons(tk.Frame):
def __init__(self, menu):
super().__init__(menu)
self.menu = menu
def process_data(self, data, operation):
operation(data)
def add(self):
data = self.menu.form.get()
exists = self.exist_check(data.get('ISBN', None))
if exists is None:
show_no_connection()
else:
if exists:
self.menu.show_error(
'Record with set ISBN already exists in database.')
else:
self.process_data(data, Database().add)
#staticmethod
def exist_check(number):
if number:
return Database().search({'ISBN': number})
return False
Displayed error:
=================================== FAILURES ===================================
_________ test_add_calls_function[True-project.gui.Gui.show_error] _________
buttons = <[AttributeError("'Buttons' object has no attribute '_w'") raised in repr()] Buttons object at 0x7f840114aa10>
value = True, patched = 'project.gui.Gui.show_error'
mocker = <pytest_mock.plugin.MockFixture object at 0x7f840114ab90>
#pytest.mark.parametrize('value,patched',(
(None, 'project.gui.show_no_connection'),
(False, 'project.gui.Buttons.process_data'),
(True, 'project.gui.Gui.show_error'),
))
def test_add_calls_function(buttons, value, patched, mocker):
mocker.patch('project.gui.Buttons.exist_check', return_value=value)
mocked = mocker.patch(patched)
buttons.add()
> mocked.assert_called_once()
E AssertionError: Expected 'show_error' to have been called once. Called 0 times.
tests/test_gui_buttons.py:88: AssertionError

I don't see a possibility to handle this in the same test - you probably will need a separate test for the last call. The problem is that the menu is already mocked, and you need that specific menu mock to do the test for the function call (the function will be called from that mock instance).
Here is a possible working implementation:
import pytest
# don't use "from project import gui" here to not make a copy in the test module
# that would be used instead of the mocked one
import project.gui
#pytest.fixture
def menu_mock(mocker):
# gives the possibility to access the menu mock
# we need the return_value to get the instance instead of the class
return mocker.patch('project.gui.Menu').return_value
#pytest.fixture
def buttons(mocker, menu_mock):
mocker.patch('project.gui.tk.Frame.__init__', return_value=None)
mocker.patch('project.gui.tk.Button')
return project.gui.Buttons(menu_mock)
#pytest.mark.parametrize('value, patched',(
(None, 'project.gui.show_no_connection'),
(False, 'project.gui.Buttons.process_data')
))
def test_add_calls_function(buttons, value, patched, mocker):
# unchanged except for the missing parametrize case
mocker.patch('project.gui.Buttons.exist_check', return_value=value)
mocked = mocker.patch(patched)
buttons.add()
mocked.assert_called_once()
def test_add_calls_show_error(buttons, menu_mock, mocker):
mocker.patch('project.gui.Buttons.exist_check', return_value=True)
buttons.add()
# you now have access to the mocked menu instance
menu_mock.show_error.assert_called_once()

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").

How to access Pytest item outside a test function

I want to retrive pytest current running test item and then append my function running data into item.user_properties.
I tried to create dummy plugin and impl pytest_runtest_protocol, update running item in side the hook, then access item from plugin from _pytest.config import get_plugin_manager, plugin_manager.get_plugin('dummy').current_running_item
but it does not work, get_plugin('dummy') return None
How can I achive this or is there a better way?
# my_package/utl.py
from _pytest.config import get_plugin_manager
def my_requester(url):
...
...
manager = get_plugin_manager()
dummy = manager.get_plugin('dummy')
item = dummy.current_item
item.user_properties.append(('requested_url',url))
# conftest.py
import pytest
class Dummy:
def __init__():
self.current_item = None
def pytest_runtest_protocol(self, item, nextitem):
self.current_item = item
def pytest_configure(config):
dummy_plugin = Dummy()
config.pluginmanager.register(dummy_plugin,'dummy')
# test_demo.py
import pytest
from my_package.utl import my_requester
def test_a():
...
my_requester('xxxx')
def test_b():
...
my_requester('yyy')
Found a solution
just use Singleton pattern, set test item to a singleton object property, and append function running data to that property

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)

python mock global function that is used in class

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')

Categories

Resources