我有一个函数(myfunc
),带有验证输入a
和c
凭据,这些凭据将设置服务以调用func_z
。
对于某些验证输入,func_z
将引发错误,在其他情况下,它将返回一些值字典。我有一个修改func_z
输出的outer_func
.
我试图这样嘲笑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, b, c):
return myfunc(a, b, c) + 1
在测试函数时,我不得不模拟c
凭据。所以我尝试用:
import pytest
from unittest.mock import MagicMock
test_data = ( (('fr', 5), 26), ('de', 7, 50), (('zh', 5), XError) )
SIDE_EFFECTS = {'fr': {'status': 'okay'}, 'de': {'status': 'okay'}, 'zh': XError}
@pytest.mark.parametrize("a, b", test_data)
def mytest(a, b, 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, b, mock_creds) == expected
不知何故,XError
没有在 pytest 中引发,输出返回('zh', 5)
输入的26
而不是 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, b, expected", happy_data)
def test_happy(mock_creds, a, b, expected):
func_z_return = SIDE_EFFECTS[a]
mock_creds.setup_client.return_value.func_z.return_value = func_z_return
assert outer_func(a, b, mock_creds) == expected
xerror_data = (('zh', 5), )
@pytest.mark.parametrize("a, b", xerror_data)
def test_xerror(mock_creds, a, b):
mock_creds.setup_client.return_value.func_z.side_effect = XError
with pytest.raises(XError):
outer_func(a, b, mock_creds)
请注意如何使用pytest.raises
上下文来测试是否在上一次测试中引发XError
。如果要测试异常详细信息,可以存储引发的异常并在with
块之后进行检查:
with pytest.raises(XError) as excinfo:
outer_func(a, b, mock_creds)
ex = excinfo.value
assert isinstance(ex, XError) # well, duh
assert ex.message == "An XError message if passed" # etc.