我正在测试一个具有多个外部依赖项的应用程序,并且我已经使用monkeypatching技术通过自定义实现来修补外部库的函数,以帮助我的测试。它的工作方式与预期一致。
但我目前遇到的问题是,这让我的测试文件变得非常混乱。我有几个测试,每个测试都需要自己的补丁函数实现。
例如,假设我有一个来自外部库的GET函数,我的test_a()需要修补GET()以便返回False,而test_b()需要修补GET()以便返回True。
处理这种情况的首选方法是什么?目前,我做了以下工作:
def test_a(monkeypatch):
my_patcher(monkeypatch, patch_get_to_return_true = True, patch_get_to_return_false = False, patch_get_to_raise_exception = False)
def test_b(monkeypatch)
my_patcher(monkeypatch, patch_get_to_return_true = True, patch_get_to_return_false = False, patch_get_to_raise_exception = False)
def test_c(monkeypatch)
my_patcher(monkeypatch, patch_get_to_return_true = False, patch_get_to_return_false = False, patch_get_to_raise_exception = True)
def my_patcher(monkeypatch, patch_get_to_return_true = False, patch_get_to_return_false = False, patch_get_to_raise_exception = False):
def patch_func_pos():
return True
patch_func_neg():
return False
patch_func_exception():
raise my_exception
if patch_get_to_return_true:
monkeypatch.setattr(ExternalLib, 'GET', patch_func_pos)
if patch_get_to_return_false:
monkeypatch.setattr(ExternalLib, 'GET', patch_func_neg)
if patch_get_to_raise_exception:
monkeypatch.setattr(ExternalLib, 'GET', patch_func_exception)上面的示例只有三个修补一个函数的测试。我的实际测试文件有大约20个测试,每个测试将进一步修补几个函数。
有人能建议我一个更好的处理方法吗?是否建议将monkeypatching部分移动到单独的文件中?
发布于 2018-07-19 01:22:57
在不了解更多细节的情况下,我建议将my_patcher拆分成几个小的fixture:
@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的不同补丁),测试或fixtures参数化通常可以节省大量代码重复。假设您有一个自己的函数在内部调用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三次,副本之间的唯一区别是使用了不同的fixture。这可以通过编写一个参数化的fixture来避免,该fixture将接受GET的多种补丁可能性
@pytest.fixture(params=[lambda: True,
lambda: False,
raise_],
ids=['pos', 'neg', 'exception'])
def mocked_GET(request):
monkeypatch.setattr(ExternalLib, 'GET', request.param)现在,在对test_inform应用mocked_GET时
@pytest.mark.usefixtures('mocked_GET')
def test_inform():
assert inform() in ['success', 'failure', 'error']您将得到三个测试:test_inform将运行三次,每个mock传递给mocked_GET参数一次。
test_inform[pos]
test_inform[neg]
test_inform[exception]测试也可以参数化(通过pytest.mark.parametrize),如果应用正确,参数化技术可以节省大量样板代码。
https://stackoverflow.com/questions/51403680
复制相似问题