如何用pytest测试Account类是否正确抛出指定异常?
问题:pytest测试Account类异常抛出失败的原因及解决方法
原Account类代码
我写了一个Account类,当账号长度超过12位时会抛出错误,代码如下:
import logging class Account: def __init__(self, acct): self.tagged = {} self.untagged = {} self.acct_stats = {} try: if len(str(acct)) < 12: prepend_digit_range = 12 - len(str(acct)) for i in range(prepend_digit_range): acct = "0" + str(acct) if len(str(acct)) > 12: raise ValueError except ValueError: logging.error("Account ID " + str(acct) + " invalid") raise self.account_id = str(acct)
尝试的测试代码及错误
我尝试用pytest测试异常抛出功能,但两次写法都失败了:
第一次尝试的测试代码
@pytest.mark.skip("WIP") @pytest.mark.parametrize( "account_id, expected", [ pytest.mark.raises( (31415926535897932384626, "Account ID 31415926535897932384626 invalid"), exception=ValueError ) ] ) def test_account_setup_raise_error1(account_id, expected): account = Account(account_id) with expected: assert account.account_id == expected
第二次尝试的测试代码
@pytest.mark.parametrize( "account_id, expected", [ ( (31415926535897932384626, "Account ID 31415926535897932384626 invalid"), ValueError, ) ], ) def test_account_setup_raise_error(account_id, expected): account = Account(account_id) with expected: assert account.account_id == expected
运行pytest得到的错误信息
/usr/local/lib/python3.10/site-packages/pluggy/_hooks.py:265: in __call__ return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult) /usr/local/lib/python3.10/site-packages/pluggy/_manager.py:80: in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) /usr/local/lib/python3.10/site-packages/_pytest/python.py:272: in pytest_pycollect_makeitem return list(collector._genfunctions(name, obj)) /usr/local/lib/python3.10/site-packages/_pytest/python.py:499: in _genfunctions self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc)) /usr/local/lib/python3.10/site-packages/pluggy/_hooks.py:292: in call_extra return self(**kwargs) /usr/local/lib/python3.10/site-packages/pluggy/_hooks.py:265: in __call__ return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult) /usr/local/lib/python3.10/site-packages/pluggy/_manager.py:80: in _hookexec return self._inner_hookexec(hook_name, methods, kwargs, firstresult) /usr/local/lib/python3.10/site-packages/_pytest/python.py:151: in pytest_generate_tests metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) /usr/local/lib/python3.10/site-packages/_pytest/python.py:1300: in parametrize argnames, parametersets = ParameterSet._for_parametrize( /usr/local/lib/python3.10/site-packages/_pytest/mark/structures.py:166: in _for_parametrize if len(param.values) != len(argnames): E TypeError: object of type 'MarkDecorator' has no len()
失败原因
- 第一个测试用例的问题:错误使用了
pytest.mark.raises,这个标记是用来装饰测试函数的,不能直接放在parametrize的参数列表里。pytest解析参数时会把它当成参数集处理,尝试获取它的长度,但MarkDecorator对象没有长度属性,因此抛出TypeError。 - 第二个测试用例的问题:参数结构错误,把
(账号ID, 错误信息)作为整体传给account_id参数,导致实例化Account时直接抛出异常,后续的with expected语句根本不会执行;同时测试逻辑错误,应该在实例化Account的代码外面包裹pytest.raises上下文管理器,而不是先实例化再处理。
正确的测试用例写法
更新后的Account类代码(新增非数字账号校验)
我后来更新了Account类,新增了非数字账号的校验逻辑:
import logging import re class Account: def __init__(self, acct): self.tagged = {} self.untagged = {} self.acct_stats = {} try: if len(str(acct)) < 12: prepend_digit_range = 12 - len(str(acct)) for i in range(prepend_digit_range): acct = "0" + str(acct) # 新增非数字校验和长度校验 if len(str(acct)) > 12 or re.match("^[0-9]+$", str(acct)) is None: raise ValueError("Account ID " + str(acct) + " invalid") except ValueError: logging.error("Account ID " + str(acct) + " invalid") raise self.account_id = str(acct)
正确的pytest测试代码
使用pytest.raises上下文管理器捕获异常,结合parametrize传递多个测试用例:
import pytest from your_module import Account # 替换成你的模块名 @pytest.mark.parametrize( "account_id", [ 31415926535897932384626, # 长度超过12位的数字 "blah" # 非数字账号 ], ) def test_account_setup_raise_error(account_id): with pytest.raises(ValueError, match="invalid"): Account(account_id)
代码说明
pytest.mark.parametrize用来传递需要测试的异常场景:超长数字、非数字账号。pytest.raises(ValueError, match="invalid")上下文管理器,用来断言实例化Account时会抛出ValueError,且异常信息包含"invalid"关键字。- 不需要先实例化Account再处理,而是将实例化代码放在
with块内,当抛出指定异常时,测试通过。
内容的提问来源于stack exchange,提问作者Burvil
相关产品推荐
相关产品推荐

