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 ImportError
s if this were the case.
Replace all instances of this import with from unittest.mock import <stuff>
.
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()
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.
For me the solution was to use 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 patched_function_to_patch:
result= my_fixture.baa('mocked_input')
assert result == 'mocked_result'
patched_function_to_patch.assert_has_calls([
call('mocked_input')
])
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 :)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.