We've recently switched from unittest to pytest. I've encountered a strange problem when using mocker.patch as a context manager. Consider the following example.
module_a.py
class MyClass:
def value(self):
return 10
module_b.py
import module_a
class AnotherClass:
def get_value(self):
return module_a.MyClass().value()
test_module_b.py
from module_b import AnotherClass
def test_main_2(mocker):
with mocker.patch('module_a.MyClass.value', return_value=20):
value = AnotherClass().get_value()
assert value == 20
value = AnotherClass().get_value()
assert value == 10
I would expect that once the context manager exits, MyClass's value method method would be restored (return value of 10), however the test fails on the second assert statement with an assertion error 20 != 10 If I use the exact same test, but replace mocker.patch with unittest.mock.patch, it passes. I thought that pytest-mock shared the same API as unittest.mock, so I'm confused as to why there is a difference.
With pytest-mock, teardown is done when exiting the fixture context. The mocker.patch object is not just a simple alias for mock.patch.
You should not need context managers within the test functions when writing pytest-style tests, and in fact the purpose of the pytest-mock plugin is to make the use of context managers and function decorators for mocking unnecessary.
If for some reason you do need a teardown step from within the test itself, then you want the plain old mock API which also works fine within pytest.
from unittest.mock import patch
def test_main_2():
with patch('module_a.MyClass.value', return_value=20):
value = AnotherClass().get_value()
assert value == 20
value = AnotherClass().get_value()
assert value == 10
Be aware that this nested structure is really what pytest intends to avoid, to make your tests more readable, so you're somewhat missing the point if you don't do setup and teardown entirely with fixtures.
Related
I have taken a below code from a large code repository of interdependent modules. I want to run this as an independent unit for testing.
In below while calling db_obj.create_connection() i have to pass the value of debug_obj which means I have to import all the dependent implementation of that which I do not want.
However, if I pass None as db_obj.create_connection(None) then it will fail at debug_obj.info('inside create_connection') due to attribute error. Unless I go and disable debug_obj.info() wherever used.
What could be the best possible way to handle such a situation where you wanted to disable you dependent library codes just for your unit testing without commenting its callings.
import pyodbc
class DBOperations(object):
def __init__(self,db_params):
self.db_params=db_params
def create_connection(self, debug_obj):
debug_obj.info('inside create_connection')
mycode_to_run_create_connection
if __name__ == '__main__':
db_obj = DBOperations(db_params ='param_for_db_connection')
db_obj.create_connection(None)
It looks like using the mock library would be a good way of dealing with a situation like yours.
In your case, a Mock or MagicMock should do the trick.
In the same place where you are instantiating the DBOperations class, you could mock the debug_obj:
from unittest.mock import Mock
...
...
if __name__ == '__main__':
debug_obj = Mock()
db_obj = DBOperations(db_params ='param_for_db_connection')
db_obj.create_connection(debug_obj)
Once the Mock object has been instantiated, attributes can be assigned to it, for example:
debug_obj = Mock()
debug.info = print
debug.info('test')
# >>> prints the word test to the stdout
I'm relatively new to pytest-style unit testing, and I'm trying to learn more about pytest fixtures. I'm not passing a scope argument to the fixture, so I know that the scope is "function". Are there any functional differences in these 3 styles of simple fixtures? Why would one approach be favored over the others?
#pytest.fixture()
#patch('a.b.c.structlog.get_logger')
def fixture_classQ(mock_log):
handler = MagicMock(spec=WidgetHandler)
return ClassQ(handler)
#pytest.fixture()
def fixture_classQ():
with patch('a.b.c.structlog.get_logger'):
handler = MagicMock(spec=WidgetHandler)
return ClassQ(handler)
#pytest.yield_fixture()
def fixture_classQ():
with patch('a.b.c.structlog.get_logger'):
handler = MagicMock(spec=WidgetHandler)
yield ClassQ(handler)
Simple example usage of the fixture:
def test_classQ_str(fixture_classQ):
assert str(fixture_classQ) == "This is ClassQ"
Thanks.
fixture 1
Starting with the first one, this creates a plain-data fixture. The mock is (imo misleadingly) only alive for the duration of the fixture function because it uses return.
In order ~roughly what happens for that:
pytest notices your fixture is used for the test function
it calls the fixture function
the mock decorator starts the patch
the mock decorator calls your actual function (which returns a value)
the mock decorator undoes the patch
pytest notices it wasn't a generator and so that's the value of your fixture
fixture 2
the second is identical in behaviour to the first, except it uses the context manager form of mock instead of the decorator. personally I don't like the decorator form but that's just me :D
fixture 3
(first before I continue, pytest.yield_fixture is a deprecated alias for pytest.fixture -- you can just use #pytest.fixture)
The third does something different! The patch is alive for the duration of the test because it has "yielded" during the fixture. This is a kind of way to create a setup + teardown fixture all in one. Here's roughly the execution here
pytest notices your fixture is used for the test function
pytest calls the fixture function
since it is a generator, it returns immediately without executing code
pytest notices it is a generator, calls next(...) on it
this causes the code to execute until the yield and then "pausing". you can think of it kind of as a co-routine
the __enter__ of the mock is called making the patch active
the value that is yielded is used as the value of the fixture
pytest then executes your test function
pytest then calls next(...) again on the generator to exhaust the fixture
this __exit__s the with statement, undoing the patch
which to choose?
the best answer is it depends. Since 1 and 2 are functionally equivalent it's up to personal preference. Pick 3. if you need the patch to be active during the entire duration of your test. And don't use pytest.yield_fixture, just use pytest.fixture.
in the case of unit testing a wrapper library, testing the wrapper without depending/exercising the upstream library is a goal; In a known case, all calls to the upstream library can be mocked and that's what I've done, but I've been frustrated by changes to the wrapper that introduce more calls to the upstream library being missed by the mock tools;
How can I best fail any test that tries to use a given namespace?
My idea currently is to change all the unittest methods to have a monkey patch like
#unittest.mock.patch('wrapper_namespace.upsteam_namespace')
and reply the upstream library with a mock that can be asserted untouched; I'm hoping for an option that works globally, so that I
don't have to add a monkeypatch to every test method, though this level of granularity is acceptable; but also don't have to perform the assertion that the mock was never used in the test methods (or make a decorator to do all that either)
prohibits access to the upstream library from any part of the software
(e.g, Wrapper calls B calls Upstream, B's call to upstream might not be caught)
You don't have to patch every test method. You can easily patch over the class if you're using unittest, or just assign the module to whatever you want to patch over it with. Here's a workable example:
A fake lib in some_lib.py:
def some_lib_func():
raise ValueError("I've been called.")
def some_other_lib_func():
raise ValueError("I've been called.")
class SomeClass:
def __init__(self):
raise ValueError("I've been constructed.")
wrapper.py:
import some_lib
def wrapper1():
some_lib.some_lib_func()
def wrapper2():
some_lib.some_other_lib_func()
def wrapper3():
x = some_lib.SomeClass()
test.py:
from unittest.mock import patch, MagicMock
import unittest
import wrapper
# Alternative:
# wrapper.some_lib = MagicMock()
# Can patch an entire class
#patch('wrapper.some_lib', MagicMock())
class TestWrapper(unittest.TestCase):
def test_wrapper1(self):
wrapper.wrapper1()
def test_wrapper2(self):
wrapper.wrapper2()
def test_wrapper3(self):
wrapper.wrapper3()
if __name__ == "__main__":
unittest.main()
We would explode if the functions/classes in some_lib were called, but they aren't:
Matthews-MacBook-Pro:stackoverflow matt$ python test.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
Feel free to comment out the patch and comment in wrapper.some_lib = MagicMock(). You'll get the same result in this toy example, but there is a major difference between the two approaches:
When using #patch('wrapper.some_lib', MagicMock()) the patch is only live for that Test Case class.
When using wrapper.some_lib = MagicMock(), however, that patch will stay live for the entire length of your python program, unless you save off the original module and patch it back manually at some point. Everything that is using the wrapper module will get the mocked version.
So you could so something like:
original_lib = wrapper.some_lib
wrapper.some_lib = MagicMock()
...
# call some test suite, every call to the wrapper module will be mocked out
...
wrapper.some_lib = original_lib
...
# call some other test suite that actually needs the real thing
...
HTH.
EDIT: Misread your question slightly, but you can inspect MagicMock objects to see if they've been called, and if so, fail the test. Or just patch over with something that fails when called (instead of MagicMock). I can provide code to do this if requested (just leave a comment), but hopefully the above can get you started. I think the crux of the question was really about the global patching. Cheers!
Pytest suite has a brilliant feature of fixtures.
To make a reusable fixture, we mark a function with special decorator:
#pytest.fixture
def fix():
return {...}
It can later be used in our test through an argument name matching the original name of the fixture:
def test_me(fix):
fix['field'] = 'expected'
assert(fix['field'] == 'expected')
Although from time to time we might forget to specify the fixture in the arguments, and, since the name of the factory matches the name of the object produced, the test will silently apply changes to the factory object itself:
def test_me(): # notice no arg
fix['this is'] = 'a hell to debug'
Certainly, the outcome is undesirable. It would be nice, for instance, to be able to add some suffix to factory function, but the pytest.fixture decorator apparently does not have a means to override the name for the fixture.
Any other advice would suffice as well.
What is a recommended technique to protect ourselves from this kind of issue?
You can use autouse=True while defining fixture to invoke the fixture every time the scope of fixture starts. This feature was added in pytest 2.0.
For example:
import pytest
#pytest.fixture(scope='function',autouse=True)
def fixture_a():
return 5
def test_a():
assert fixture_a == 5
As you can see, I did not have to declare fixture as an argument in test_a to access it.
Documentation: https://docs.pytest.org/en/latest/reference.html#pytest-fixture
Code example: https://docs.pytest.org/en/latest/fixture.html#autouse-fixtures-xunit-setup-on-steroids
I have encountered something mysterious, when using patch decorator from mock package integrated with pytest fixture.
I have two modules:
-----test folder
-------func.py
-------test_test.py
in func.py:
def a():
return 1
def b():
return a()
in test_test.py:
import pytest
from func import a,b
from mock import patch,Mock
#pytest.fixture(scope="module")
def brands():
return 1
mock_b=Mock()
#patch('test_test.b',mock_b)
def test_compute_scores(brands):
a()
It seems that patch decorate is not compatible with pytest fixture. Does anyone have a insight on that? Thanks
When using pytest fixture with mock.patch, test parameter order is crucial.
If you place a fixture parameter before a mocked one:
from unittest import mock
#mock.patch('my.module.my.class')
def test_my_code(my_fixture, mocked_class):
then the mock object will be in my_fixture and mocked_class will be search as a fixture:
fixture 'mocked_class' not found
But, if you reverse the order, placing the fixture parameter at the end:
from unittest import mock
#mock.patch('my.module.my.class')
def test_my_code(mocked_class, my_fixture):
then all will be fine.
As of Python3.3, the mock module has been pulled into the unittest library. There is also a backport (for previous versions of Python) available as the standalone library mock.
Combining these 2 libraries within the same test-suite yields the above-mentioned error:
E fixture 'fixture_name' not found
Within your test-suite's virtual environment, run pip uninstall mock, and make sure you aren't using the backported library alongside the core unittest library. When you re-run your tests after uninstalling, you would see ImportErrors if this were the case.
Replace all instances of this import with from unittest.mock import <stuff>.
Hopefully this answer on an old question will help someone.
First off, the question doesn't include the error, so we don't really know what's up. But I'll try to provide something that helped me.
If you want a test decorated with a patched object, then in order for it to work with pytest you could just do this:
#mock.patch('mocked.module')
def test_me(*args):
mocked_module = args[0]
Or for multiple patches:
#mock.patch('mocked.module1')
#mock.patch('mocked.module')
def test_me(*args):
mocked_module1, mocked_module2 = args
pytest is looking for the names of the fixtures to look up in the test function/method. Providing the *args argument gives us a good workaround the lookup phase. So, to include a fixture with patches, you could do this:
# from question
#pytest.fixture(scope="module")
def brands():
return 1
#mock.patch('mocked.module1')
def test_me(brands, *args):
mocked_module1 = args[0]
This worked for me running python 3.6 and pytest 3.0.6.
If you have multiple patches to be applied, order they are injected is important:
# from question
#pytest.fixture(scope="module")
def brands():
return 1
# notice the order
#patch('my.module.my.class1')
#patch('my.module.my.class2')
def test_list_instance_elb_tg(mocked_class2, mocked_class1, brands):
pass
This doesn't address your question directly, but there is the pytest-mock plugin which allows you to write this instead:
def test_compute_scores(brands, mock):
mock_b = mock.patch('test_test.b')
a()
a) For me the solution was to use a with block inside the test function instead of using a #patch decoration before the test function:
class TestFoo:
def test_baa(self, my_fixture):
with patch(
'module.Class.function_to_patch',
MagicMock(return_value='mocked_result')
) as mocked_function_to_patch:
result= my_fixture.baa('mocked_input')
assert result == 'mocked_result'
mocked_function_to_patch.assert_has_calls([
call('mocked_input')
])
This solution does work inside classes (that are used to structure/group my test methods). Using the with block, you don't need to worry about the order of the arguments. I find it more explicit then the injection mechanism but the code becomes ugly if you patch more then one variable. If you need to patch many dependencies, that might be a signal that your tested function does too many things and that you should refactor it, e.g. by extracting some of the functionality to extra functions.
b) If you are outside classes and do want a patched object to be injected as extra argument in a test method... please note that #patch does not support to define the mock as second argument of the decoration:
#patch('path.to.foo', MagicMock(return_value='foo_value'))
def test_baa(self, my_fixture, mocked_foo):
does not work.
=> Make sure to pass the path as only argument to the decoration. Then define the return value inside the test function:
#patch('path.to.foo')
def test_baa(self, my_fixture, mocked_foo):
mocked_foo.return_value = 'foo_value'
(Unfortunately, this does not seem to work inside classes.)
First let inject the fixture(s), then let inject the variables of the #patch decorations (e.g. 'mocked_foo').
The name of the injected fixture 'my_fixture' needs to be correct. It needs to match the name of the decorated fixture function (or the explicit name used in the fixture decoration).
The name of the injected patch variable 'mocked_foo' does not follow a distinct naming pattern. You can choose it as you like, independent from the corresponding path of the #patch decoration.
If you inject several patched variables, note that the order is reversed: the mocked instance belonging to the last #patch decoration is injected first:
#patch('path.to.foo')
#patch('path.to.qux')
def test_baa(self, my_fixture, mocked_qux, mocked_foo):
mocked_foo.return_value = 'foo_value'
I had the same problem and solution for me was to use mock library in 1.0.1 version (before I was using unittest.mock in 2.6.0 version). Now it works like a charm :)