I am testing functions from different modules that use an imported helper function from helpers.py
. I am monkeypatching this helper function in each module function's test (please see below). I would like to put setup_do_thing()
and mock_do_thing()
in a conftest.py
file to cut down on repetition and remove general_mocks.py
, but am unable to do this due to the different import paths supplied to monkeypatch.setattr()
in test_module_1.py
and test_module_2.py
.
Is there a way to put the fixture in a single file and supply to multiple test modules?
app/helper.py
:
def do_thing():
#do_thing
app/module1.py
:
from helper import do_thing
def use_do_thing_for_x():
...
do_thing()
...
app/module2.py
:
from helper import do_thing
def use_do_thing_for_y():
...
do_thing
...
tests/general_mocks.py
:
def mock_do_thing():
#do_other_thing
tests/test_module_1.py
:
from general_mocks import mock_do_thing
@pytest.fixture
def setup_do_thing(monkeypatch):
monkey_patch.setattr('app.module1.do_thing',
mock_do_thing)
def test_use_do_thing_for_x(setup_do_thing):
...
tests/test_module_2.py
:
from general_mocks import mock_do_thing
@pytest.fixture
def setup_do_thing(monkeypatch):
monkey_patch.setattr('app.module2.do_thing',
mock_do_thing)
def test_use_do_thing_for_y(setup_do_thing):
...
If you want to do this, you have to parametrize your fixture in some way, as it cannot guess the correct module. First comes to mind indirect parametrization, e.g. something like:
conftest.py
@pytest.fixture
def setup_do_thing(monkeypatch, request):
# error handling omitted
monkeypatch.setattr(f"app.{request.param}.do_thing",
mock_do_thing)
test_module1.py
@pytest.mark.parametrize("setup_do_thing", ["module1"], indirect=True)
def test_use_do_thing_for_x(setup_do_thing):
use_do_thing_for_x()
If you don't like because it somewhat misuses parametrization, you can for example define your own marker instead:
conftest.py
def pytest_configure(config):
# register your marker
config.addinivalue_line(
"markers", "do_thing_module: module where 'do_thing' resides"
)
@pytest.fixture
def setup_do_thing(monkeypatch, request):
mark = request.node.get_closest_marker("do_thing_module")
if mark:
# use the first marker argument as module name
# error handling omitted
monkeypatch.setattr(f"app.{mark.args[0]}.do_thing",
mock_do_thing)
test_module1.py
@pytest.mark.do_thing_module("module1")
def test_use_do_thing_for_x(setup_do_thing):
use_do_thing_for_x()
There are probably more possibilities to do this, but these came to mind first.