我正在测试一个具有多个外部依赖项的应用程序,并且我使用了monkeypatching技术通过自定义实现来修补外部库的功能,以帮助测试。它按预期工作。
但是我目前遇到的问题是,这使我的测试文件非常混乱。我有几个测试,每个测试都需要自己执行补丁功能。
例如,假设我有一个来自外部库的GET函数,我的test_a()
需要修补GET()
,以便它返回False,而test_b()
需要{{1} }进行修补,使其返回True。
处理这种情况的首选方法是什么。目前,我正在执行以下操作:
GET()
上面的示例只有三个测试可以修补一个功能。我的实际测试文件有大约20个测试,每个测试将进一步修补几个功能。
有人可以建议我一种更好的方法吗?是否建议将Monkeypatching部分移动到单独的文件中?
答案 0 :(得分:1)
在不知道更多细节的情况下,我建议将my_patcher
分成几个小的装置:
@pytest.fixture
def mocked_GET_pos(monkeypatch):
monkeypatch.setattr(ExternalLib, 'GET', lambda: True)
@pytest.fixture
def mocked_GET_neg(monkeypatch):
monkeypatch.setattr(ExternalLib, 'GET', lambda: False)
@pytest.fixture
def mocked_GET_raises(monkeypatch):
def raise_():
raise Exception()
monkeypatch.setattr(ExternalLib, 'GET', raise_)
现在使用pytest.mark.usefixtures
自动应用测试中的灯具:
@pytest.mark.usefixtures('mocked_GET_pos')
def test_GET_pos():
assert ExternalLib.GET()
@pytest.mark.usefixtures('mocked_GET_neg')
def test_GET_neg():
assert not ExternalLib.GET()
@pytest.mark.usefixtures('mocked_GET_raises')
def test_GET_raises():
with pytest.raises(Exception):
ExternalLib.GET()
但是,根据实际情况,仍有改进的空间。例如,当测试逻辑相同并且唯一不同的是某些测试前提条件(例如您的案例中GET
的不同修补)时,测试或固定装置的参数化通常可以节省大量代码重复。假设您有一个内部调用GET
的函数:
# my_lib.py
def inform():
try:
result = ExternalLib.GET()
except Exception:
return 'error'
if result:
return 'success'
else:
return 'failure'
,并且您想测试无论GET
的行为如何,它是否都返回有效结果:
# test_my_lib.py
def test_inform():
assert inform() in ['success', 'failure', 'error']
使用上述方法,您需要复制test_inform
三遍,副本之间的唯一区别是所使用的夹具不同。可以通过编写参数化的夹具来避免这种情况,该夹具将接受GET
的多种补丁可能性:
@pytest.fixture(params=[lambda: True,
lambda: False,
raise_],
ids=['pos', 'neg', 'exception'])
def mocked_GET(request):
monkeypatch.setattr(ExternalLib, 'GET', request.param)
现在将mocked_GET
应用于test_inform
时:
@pytest.mark.usefixtures('mocked_GET')
def test_inform():
assert inform() in ['success', 'failure', 'error']
您从一项测试中获得了三项测试:test_inform
将运行三遍,一次将每个模拟传递给mocked_GET
参数。
test_inform[pos]
test_inform[neg]
test_inform[exception]
也可以对测试进行参数化(通过pytest.mark.parametrize
),正确应用参数化技术可以节省大量样板代码。