Consider my module "mymodule.py"
# contents of "mymodule.py"
def func1(x):
return x * 2
I want to mock this function and alter its return. Per the documentation I can do this:
# contents of "test_mymodule.py"
import mymodule
import pytest
@pytest.fixture
def mock_func1():
def mock_ret(*args, **kwargs):
return 2
def test_func1_a(monkeypatch, mock_func1):
monkeypatch.setattr(mymodule, "func1", mock_func1)
assert mymodule.func1(1) == 2
def test_func1_b(monkeypatch, mock_func1):
monkeypatch.setattr(mymodule, "func1", mock_func1)
assert mymodule.func1(1) != 37
However, I don't want to monkey patch the module for each test. What is the proper way to monkeypatch.setattr
once for the scope of the whole test module test_mymodule.py
?
I'd expect something like this
# contents of "test_mymodule.py"
import mymodule
import pytest
@pytest.fixture
def mock_func1():
def mock_ret(*args, **kwargs):
return 2
monkeypatch.setattr(mymodule, "func1", mock_func1)
def test_func1_a():
assert mymodule.func1(1) == 2
def test_func1_b():
assert mymodule.func1(1) != 37
But this gets me
NameError: name 'monkeypatch' is not defined
Just came across this answer because I was trying to do something similar. You can use decorators like this to do preprocessing of the tests. Other pytest decorators can be added below the mock_func_1_in_test
decorator.
# contents of "test_mymodule.py"
import mymodule
import pytest
@pytest.fixture
def mock_func1():
def mock_ret(*args, **kwargs):
return 2
return mock_ret
def mock_func_1_in_test(func):
def inner(monkeypatch, mock_func1, *args, **kwargs):
monkeypatch.setattr(mymodule, "func1", mock_func1)
return func(*args, **kwargs)
return inner
@mock_func_1_in_test
def test_func1_a():
assert mymodule.func1(1) == 2
@mock_func_1_in_test
def test_func1_b():
assert mymodule.func1(1) != 37
This works as you'd expect:
$ pytest
================================== test session starts ===================================
platform darwin -- Python 3.6.6, pytest-3.6.0, py-1.6.0, pluggy-0.6.0
rootdir: /Users/delgadom/git/messin/pytest_test, inifile:
plugins: cov-2.5.1
collected 2 items
test_mymodule.py .. [100%]
================================ 2 passed in 0.03 seconds ================================
Stolen directly from pytest:
import mymodule
import pytest
def wildpatch(target, name, value=None, raising=True):
import inspect
if value is None:
if not isinstance(target, _basestring):
raise TypeError("use setattr(target, name, value) or "
"setattr(target, value) with target being a dotted "
"import string")
value = name
name, target = derive_importpath(target, raising)
oldval = getattr(target, name, None)
if raising and oldval is None:
raise AttributeError("%r has no attribute %r" % (target, name))
# avoid class descriptors like staticmethod/classmethod
if inspect.isclass(target):
oldval = target.__dict__.get(name, None)
setattr(target, name, value)
##@pytest.fixture
##def mock_func1():
## def mock_ret(*args, **kwargs):
## print("monkeypatched func1")
## return 2
def mock_func1(*args, **kwargs):
print("monkeypatched func1")
return 2
wildpatch(mymodule, "func1", mock_func1)
def test_func1_a():
print("Running test_func1_a")
assert mymodule.func1(1) == 2
def test_func1_b():
assert mymodule.func1(1) != 37
On running with python -m pytest -s test.py
yields
=============================== test session starts ================
platform linux -- Python 3.4.3, pytest-3.1.2, py-1.4.34, pluggy-0.4.0
rootdir: /tmp/ab, inifile:
collected 2 items
test.py Running test_func1_a
monkeypatched func1
.monkeypatched func1
.
=========================== 2 passed in 0.01 seconds ===============================
I have guessed all you want is too redirect func1 to your own function.
This is what I do to keep things slightly clean. Just define a pytest fixture to patch the module in conftest.py. But still, this fixture needs to be passed to each of our tests.
import mymodule
import pytest
@pytest.fixture(autouse=True)
def patch_mymodule(monkeypatch):
def mock_func1(*args, **kwargs):
return 2
monkeypatch.setattr(mymodule, "func1", mock_func1)
def func1(x):
return x * 2
import mymodule
import pytest
def test_func1_a(patch_mymodule):
# you can pass anything to the function below, 1 is just some arbitrary choice
assert mymodule.func1(1) == 2
def test_func1_b(patch_mymodule):
assert mymodule.func1(1) != 37
Just add autouse=True
as an argument to the fixture decorator.
@pytest.fixture(autouse=True)
def mock_func1():
def mock_ret(*args, **kwargs):
return 2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With