Python单元测试:如何逐行验证函数多次终端print的输出正确性?
你用错了mock的断言方法。assert_called_with() 的校验逻辑是只检查mock对象最后一次的调用入参是否匹配预期,完全不会遍历之前的所有历史调用记录。
你写的state_game函数连续触发了5次print调用,mock存储的最后一次调用就是打印分数提示的那行。当你第一次执行mock_print.assert_called_with(grid[0])时,mock只会拿最后一次(分数行)的参数和你传入的grid[0]做对比,自然会触发断言失败,这和你看到的报错现象完全一致。
要逐行匹配所有print输出的内容,核心是读取mock对象存储的全量历史调用列表,不能只依赖assert_called_with。常用的实现方式有两种:
1. 全量匹配打印顺序和内容(推荐)
mock对象的call_args_list属性,会按照print实际调用的先后顺序,存储每一次调用的入参。你只需要把预期的打印内容按顺序整理成同结构的列表,和call_args_list做等价性比对,就能一次性同时校验“每一行内容正确、打印顺序没有错乱”两个要求。
完整可运行的测试代码示例:
import unittest from unittest.mock import patch # 被测函数 def state_game(grid, score): print(grid[0]) print(grid[1]) print(grid[2]) print(grid[3]) print(f"Your score is {score}") return('------------') class TestStateGame(unittest.TestCase): @patch("__main__.print") def test_state_game(self, mock_print): # 构造固定测试输入,避免测试依赖外部数据 test_grid = [ "第一行网格内容", "第二行网格内容", "第三行网格内容", "第四行网格内容" ] test_score = 128 # 执行被测逻辑 state_game(test_grid, test_score) # 按照实际打印顺序整理预期的调用记录 expected_print_records = [ (test_grid[0],), (test_grid[1],), (test_grid[2],), (test_grid[3],), (f"Your score is {test_score}",) ] # 全量校验:顺序、每一行内容完全匹配才算通过 self.assertEqual(mock_print.call_args_list, expected_print_records)
2. 按调用索引逐行单独断言
如果只需要对某几行输出做针对性校验,不需要做全量匹配,可以直接按调用顺序的索引,从call_args_list里取出对应次的调用记录单独比对:
# 校验第一次print(索引从0开始)的输出 self.assertEqual(mock_print.call_args_list[0].args, (test_grid[0],)) # 校验最后一次(第5次,索引为4)print的分数行输出 self.assertEqual(mock_print.call_args_list[4].args, (f"Your score is {test_score}",))
注意:使用
patch捕获print调用时,打补丁的路径必须和被测代码实际引用print的位置一致。如果state_game是从其他模块导入的,需要将补丁打到被导入模块的print引用上,而非测试文件本身的print,否则会出现捕获不到调用记录的问题。
内容的提问来源于stack exchange,提问作者Nmd-AlM

