问题描述
我有一个函数 (myfunc
),带有验证输入 a
和 c
凭据,用于设置调用 func_z
的服务。
对于某些验证输入,func_z
会抛出错误,而在其他情况下,它会返回一些值的字典。我有一个 outer_func
可以修改 func_z
的输出。
我试图这样模拟 func_z
:
class XError(Excception):
pass
def myfunc(a,b,c):
x = c.setup_client('x')
try:
x.func_z(a) # Check if x.func_z(a) works proper
except:
raise XError
return b**2
def outer_func(a,c):
return myfunc(a,c) + 1
在测试该函数时,我不得不模拟 c
凭据。所以我尝试测试:
import pytest
from unittest.mock import Magicmock
test_data = ( (('fr',5),26),('de',7,50),(('zh',XError) )
SIDE_EFFECTS = {'fr': {'status': 'okay'},'de': {'status': 'okay'},'zh': XError}
@pytest.mark.parametrize("a,b",test_data)
def mytest(a,expected):
mock_creds = Magicmock()
mock_service = Magicmock()
mock_creds.setup_client.return_value = mock_service
mock_service.func_z.return_value = SIDE_EFFECTS[a]
assert outer_func(a,mock_creds) == expected
不知何故,在 pytest 中没有引发 XError
,对于 26
输入,输出返回 ('zh',5)
而不是 XError。
但好像我没有在嘲笑任何东西。
我是否错误地使用了模拟对象中的返回值?
是否可以允许和检查在 pytest 中使用模拟对象引发的错误或输出?
解决方法
有问题的测试有两个问题。
-
在模拟中返回异常:
mock_service.func_z.return_value = XError
实际上是一个
def func_z(): return XError
这肯定不是你想要的。相反,您希望
func_z
模拟提出一个错误;为此,您需要使用side_effect
:mock_service.func_z.side_effect = XError
-
断言异常是从测试函数返回的:
outer_func
不会返回异常,而是引发异常,所以assert outer_func(a,b,mock_creds) == expected
对于
(('zh',5),XError)
参数将失败,因为outer_func
不会返回。
相反,编写两个单独的测试,因为代码可以在 myfunc
中采用两条不同的路径; test_happy
无一例外地覆盖了路径,出现错误的路径由 test_xerror
覆盖。两个测试(组装 mock_service
)共有的代码移出到一个装置中。
@pytest.fixture
def mock_creds():
mock_creds = MagicMock()
mock_service = MagicMock()
mock_creds.setup_client.return_value = mock_service
return mock_creds
happy_data = (('fr',5,26),('de',7,50))
SIDE_EFFECTS = {'fr': {'status': 'okay'},'de': {'status': 'okay'}}
@pytest.mark.parametrize("a,expected",happy_data)
def test_happy(mock_creds,a,expected):
func_z_return = SIDE_EFFECTS[a]
mock_creds.setup_client.return_value.func_z.return_value = func_z_return
assert outer_func(a,mock_creds) == expected
xerror_data = (('zh',)
@pytest.mark.parametrize("a,b",xerror_data)
def test_xerror(mock_creds,b):
mock_creds.setup_client.return_value.func_z.side_effect = XError
with pytest.raises(XError):
outer_func(a,mock_creds)
请注意 pytest.raises
上下文如何用于测试 XError
是否在上次测试中引发。如果要测试异常详细信息,可以存储引发的异常并在 with
块之后检查它:
with pytest.raises(XError) as excinfo:
outer_func(a,mock_creds)
ex = excinfo.value
assert isinstance(ex,XError) # well,duh
assert ex.message == "An XError message if passed" # etc.