如何用mock_open按文件名返回不同模拟文件测试Python函数
问题:测试时根据传入
open的文件名返回不同模拟文件 原业务代码:
from smart_open import open import pandas as pd def do_something(df): # 实际业务逻辑,示例为求和 return df['test'].sum() def do_something_else(df): # 实际业务逻辑,示例为求均值 return df['test'].mean() def foo(): with open('file1.txt', 'r') as f: df1 = pd.read_csv(f) value1 = do_something(df1) with open('file2.txt', 'r') as f: df2 = pd.read_csv(f) value2 = do_something_else(df2) return value1, value2
需求:不读取真实文件,通过mock_open根据不同文件名返回对应模拟数据,完成foo函数的测试。
错误原因分析
你之前的测试代码存在两个核心问题:
forward_filename直接将文件名作为read_data传入mock_open,导致模拟文件对象返回的是文件名字符串,而非CSV格式内容,pd.read_csv无法解析。read_sample_data错误地将文件名作为pd.read_csv的参数处理,但实际pd.read_csv接收的是文件对象,而非文件名。
正确实现方式(推荐)
核心思路:给open的mock设置side_effect,根据传入的文件名返回包含对应CSV内容的模拟文件对象,让pd.read_csv可以正常读取解析。
测试代码:
from unittest.mock import patch, mock_open import pandas as pd from your_module import foo # 替换为foo函数所在的实际模块名 def test_foo(): # 定义每个文件名对应的CSV格式内容(需与业务代码中read_csv的解析规则匹配) file_contents = { 'file1.txt': 'test\n1\n2\n', 'file2.txt': 'test\n3\n4\n' } def mock_open_by_filename(filename, *args, **kwargs): # 根据文件名返回对应的模拟文件对象 return mock_open(read_data=file_contents[filename]).return_value # 替换业务模块中的open函数 with patch('your_module.open', side_effect=mock_open_by_filename): value1, value2 = foo() # 验证结果符合预期 assert value1 == 3 # 1+2的求和结果 assert value2 == 3.5 # 3+4的均值结果
另一种思路:直接mock pd.read_csv
如果不想处理文件对象的模拟,可以通过跟踪open的调用记录获取文件名,再让pd.read_csv返回对应DataFrame:
from unittest.mock import patch, call import pandas as pd from your_module import foo def test_foo_alternative(): # 定义文件名与对应测试DataFrame的映射 file_data = { 'file1.txt': pd.DataFrame({'test': [1,2]}), 'file2.txt': pd.DataFrame({'test': [3,4]}) } # 记录open的调用参数 open_calls = [] def track_open(filename, *args, **kwargs): open_calls.append(filename) # 返回空模拟文件对象,仅用于满足上下文语法要求 return mock_open().return_value def mock_read_csv(*args, **kwargs): # 取出最近一次open调用的文件名,返回对应测试数据 filename = open_calls.pop(0) return file_data[filename] with patch('your_module.open', side_effect=track_open), \ patch('your_module.pd.read_csv', side_effect=mock_read_csv): value1, value2 = foo() assert value1 == 3 assert value2 == 3.5 # 验证open的调用次数和参数是否正确 assert your_module.open.call_count == 2 assert your_module.open.call_args_list == [call('file1.txt', 'r'), call('file2.txt', 'r')]
内容的提问来源于stack exchange,提问作者Lukas Hestermeyer
相关产品推荐
相关产品推荐

