应将生成器转为列表后断言:list(gen_func()) == [expected1, expected2, ...];遇异常需用 next() 手动迭代配合 pytest.raises();验证惰性执行需观测副作用;参数化时须传函数及参数而非已执行生成器。

pytest 怎么断言 yield 函数逐个产出的值?
直接用 assert 比较生成器对象本身没用——它只是个迭代器,不是结果列表。必须消费它才能拿到值。最稳妥的做法是把生成器转成列表再比对,但要注意:这会一次性耗尽生成器,且无法验证“按序产出”这个行为本身。
- 推荐写法:
list(your_generator_function()) == [expected1, expected2, ...] - 如果函数有副作用(比如 yield 同时修改了外部状态),转列表会触发全部执行,这点要提前意识到
- 别用
next()手动取值做多次 assert —— 既啰嗦又难维护,还容易因调用顺序错乱导致后续断言失败
测试中途抛异常的 yield 函数怎么写?
有些生成器会在某次 yield 后抛出异常(比如读文件到一半出错),这时不能靠 list() 包裹,否则异常会被吞掉或位置错乱。得用 pytest.raises() 配合手动迭代。
- 正确方式:用
iter_obj = your_generator_function()获取生成器,然后next(iter_obj)取前几个值,再用pytest.raises(YourException)包住下一次next(iter_obj) - 错误示例:
with pytest.raises(...): list(...)——list()内部调用__next__是黑盒,异常发生点不可控 - 注意:生成器一旦抛出异常就终止,后续再
next()会触发StopIteration,不是原异常
如何验证 yield 的“惰性执行”特性?
生成器的核心价值之一是延迟计算。如果测试只关心最终结果,很容易忽略这点。真要验证“没调用 next 就不执行”,得靠 side effect 观察。
- 在生成器函数里插入可观测操作,比如写日志、改全局变量、或用
print()(配合capfdfixture 捕获) - 先调用函数得到生成器对象,确认无输出;再
next()一次,检查是否只输出第一个 yield 对应的日志 - 别依赖时间测量或 CPU 占用——太不稳定,且跟“惰性”无直接因果
参数化测试 yield 函数时要注意什么?
用 @pytest.mark.parametrize 测试不同输入对应的 yield 序列时,最容易踩的坑是:参数里传了生成器对象本身,而不是能重新构造它的函数或参数组合。
立即学习“Python免费学习笔记(深入)”;
- 错误写法:
@pytest.mark.parametrize("gen", [gen1(), gen2()])—— 这里gen1()已被立即执行,传进去的是已耗尽的生成器 - 正确写法:
@pytest.mark.parametrize("func,args,expected", [(my_gen, (1,), [1]), (my_gen, (2,), [2,4])]),然后在测试函数里调用list(func(*args)) - 如果生成器内部依赖随机或时间,记得在参数化前固定 seed 或 mock 时间,否则测试可能偶然失败
list(gen_func()) == [...] 就够了;但只要涉及异常、状态变更、或想确认执行时机,就得退回到手动 next() + pytest.raises() 的组合。生成器测试的关键不是“多 fancy”,而是清楚每一步到底触发了什么。


















