Python多with open语句Mock失败求助:两次Patch返回相同数据
解决Python中Mock多次
open调用返回不同内容的问题 你用两个@patch('builtins.open')装饰器的方式行不通,因为多次patch同一个对象时,后面的装饰器会覆盖前面的。Python装饰器的执行顺序是从下到上(最靠近测试函数的先生效),所以最终只有最上方的patch生效,导致两次open调用都返回{"hello":"yes"}。
正确做法:用单个patch + side_effect控制多次调用的返回值
我们可以给mock_open返回的文件对象的read()方法设置side_effect,让它按调用顺序返回不同的JSON字符串,这样第一次open(second()里的调用)返回第一个内容,第二次(first()里的调用)返回第二个内容。
示例代码(修改原函数以便测试):
from unittest.mock import patch, mock_open import json # 假设path已定义 path = "test_file.json" def second(): with open(path, 'r') as second_something: second_json = json.load(second_something) return second_json # 添加返回值,方便测试断言 def first(): second() with open(path, 'r') as first_something: first_json = json.load(first_something) return first_json # 添加返回值,方便测试断言 class TestFirst: @patch('builtins.open', new_callable=mock_open) def test_first(self, mock_file): # 设置read()方法的顺序返回值 mock_file.return_value.read.side_effect = ['{"hello":"yes"}', '{"world":"no"}'] # 调用待测试函数 result = first() # 断言结果符合预期 assert result == {"world": "no"} # 验证open被正确调用两次 mock_file.assert_has_calls([ patch.call(path, 'r'), patch.call(path, 'r') ])
不修改原函数的测试方案:
如果不想修改原函数的返回值,可以同时mockjson.load,通过它的调用记录来验证两次open读取的内容:
from unittest.mock import patch, mock_open import json path = "test_file.json" def second(): with open(path, 'r') as second_something: second_json = json.load(second_something) def first(): second() with open(path, 'r') as first_something: first_json = json.load(first_something) class TestFirst: @patch('json.load') @patch('builtins.open', new_callable=mock_open) def test_first(self, mock_file, mock_json_load): # 设置read()的顺序返回值 mock_file.return_value.read.side_effect = ['{"hello":"yes"}', '{"world":"no"}'] first() # 验证json.load被调用两次,且内容符合预期 assert mock_json_load.call_count == 2 # 检查第一次调用的内容 first_call_content = mock_json_load.call_args_list[0][0][0].read() assert json.loads(first_call_content) == {"hello": "yes"} # 检查第二次调用的内容 second_call_content = mock_json_load.call_args_list[1][0][0].read() assert json.loads(second_call_content) == {"world": "no"}
关键说明:
side_effect可以接收一个列表,每次调用对应的方法时,会按顺序返回列表中的下一个值。- 不要重复patch同一个内置对象(比如
builtins.open),否则后一次patch会完全覆盖前一次的配置。
内容的提问来源于stack exchange,提问作者Bayare Deepak Singh
相关产品推荐
相关产品推荐

