如何修补多个测试的函数

时间:2018-06-13 17:22:11

标签: python pytest monkeypatching

考虑我的模块"mymodule.py"

# contents of "mymodule.py"

def func1(x):
    return x * 2

我想模仿这个函数并改变它的返回。根据文档,我可以这样做:

# 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 

但是,我不想为每个测试修补模块。对于整个测试模块monkeypatch.setattr的范围,test_mymodule.py一次的正确方法是什么?

我期待这样的事情

# 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 

但是这让我

NameError: name 'monkeypatch' is not defined

2 个答案:

答案 0 :(得分:1)

直接从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

运行python -m pytest -s test.py收益

=============================== 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 ===============================

我猜你想要的只是将func1重定向到你自己的函数。

答案 1 :(得分:1)

只是碰到了这个答案,因为我正在尝试做类似的事情。您可以使用这样的装饰器对测试进行预处理。可以在mock_func_1_in_test装饰器下面添加其他pytest装饰器。

# 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 

这可以按您期望的那样工作:

$ 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 ================================