You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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()

失败原因

  1. 第一个测试用例的问题:错误使用了pytest.mark.raises,这个标记是用来装饰测试函数的,不能直接放在parametrize的参数列表里。pytest解析参数时会把它当成参数集处理,尝试获取它的长度,但MarkDecorator对象没有长度属性,因此抛出TypeError。
  2. 第二个测试用例的问题:参数结构错误,把(账号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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 00:35:25