如何通过Mock确定性测试非确定性函数的异常?断言方式是否正确?
关于Mock触发异常测试及异常验证的解答
一、你的Mock实现是正确的
你当前的测试代码已经可以通过Mock myfuncs.func_a 返回{"input": 0}来确定性触发异常,不需要反复调用碰运气。这段代码逻辑没问题,执行后会进入func_c的except ZeroDivisionError分支,抛出你定义的异常。
二、断言错误消息的方式可行,但可以更健壮
当前方式的合理性
直接断言错误消息内容是有效的验证手段,能确认抛出的是预期的异常而非其他意外错误。不过有两个优化点:
- 避免精确匹配全字符串:如果后续修改错误消息的细节(比如标点、措辞),精确匹配会导致测试失败。建议用
assert "关键词" in str(e)的方式,你用的startswith也可以,只要能匹配核心信息即可。 - 优先使用自定义异常类:抛出通用的
Exception不够精准,可能会捕获到其他地方意外抛出的Exception。更严谨的做法是定义专属异常类:
优化方案:自定义异常类
- 在
myfuncs.py中添加自定义异常:
class ZeroSumDivisionError(Exception): """当func_a返回0导致除零错误时抛出的异常""" pass
- 修改
func_c的异常抛出逻辑:
def func_c(z): bz = func_b(z) scale_x = bz['b_input'] = copy.deepcopy(bz['input']) try: bz['input'] = scale_x / (scale_x *2)**2 except ZeroDivisionError as e: # 使用raise ... from e保留异常链,便于调试 raise ZeroSumDivisionError("You've lucked out, the pick from func_a gave you 0!") from e return bz
- 测试时直接断言异常类型(更精准):
用pytest的写法:
from mock import patch import pytest from myfuncs import func_c, ZeroSumDivisionError def test_func_c_zero_sum_exception(): with patch("myfuncs.func_a", return_value={"input": 0}): # 精准捕获自定义异常类 with pytest.raises(ZeroSumDivisionError) as exc_info: func_c(12) # 可选:验证消息内容 assert "You've lucked out" in str(exc_info.value)
用unittest的写法:
import unittest from mock import patch from myfuncs import func_c, ZeroSumDivisionError class TestFuncC(unittest.TestCase): def test_zero_sum_division_exception(self): with patch("myfuncs.func_a", return_value={"input": 0}): with self.assertRaises(ZeroSumDivisionError) as context: func_c(12) self.assertIn("You've lucked out", str(context.exception))
总结
- 你当前的Mock实现完全可以实现确定性异常测试,无需修改;
- 断言错误消息内容是有效的,但自定义异常类+断言异常类型的方式更健壮、更易维护;
- 若暂时不想定义自定义异常,保留当前的消息断言逻辑即可,建议用包含关键词的匹配方式替代精确匹配。
内容的提问来源于stack exchange,提问作者alvas
相关产品推荐
相关产品推荐

