如何为接收多文件的Python函数编写单元测试,模拟多个测试文件
Python单元测试模拟多文件入参方案
问题原因
- 未mock
os.path.isfile方法:函数执行第一步就会校验入参是否为存在的文件,未mock的情况下对任意假文件名都会返回False,直接抛出FileNotFoundError - patch对象误用:
mock.patch返回的是补丁控制对象,不能直接作为文件路径传入函数,需要自定义假文件名,让mock逻辑按文件名匹配对应返回内容
实现方案
以下是可直接运行的测试代码:
import os import unittest from unittest import mock # 此处替换为你自己的read_files函数导入路径 from your_module import read_files class ReadFilesTestCase(unittest.TestCase): def setUp(self): # 自定义三个假文件名作为入参 self.fake_start_log = "fake_start.log" self.fake_end_log = "fake_end.log" self.fake_abbr_file = "fake_abbr.txt" # 定义三个文件对应的测试内容 self.start_log_content = """SVF2018-05-24_12:02:58.917 NHR2018-05-24_12:02:49.914 """ self.end_log_content = """SVF2018-05-24_1:04:11.332 NHR2018-05-24_1:04:02.979 """ self.abbr_content = """SVF_Sebastian Vettel_FERRARI NHR_Nico Hulkenberg_RENAULT """ # 期望的返回结果 self.expected_result = ( [('SVF', '2018-05-24_12:02:58.917'), ('NHR', '2018-05-24_12:02:49.914')], [('SVF', '2018-05-24_1:04:11.332'), ('NHR', '2018-05-24_1:04:02.979')], [['SVF', 'Sebastian Vettel', 'FERRARI'], ['NHR', 'Nico Hulkenberg', 'RENAULT']] ) def test_read_files_normal_case(self): # 1. mock os.path.isfile,仅对测试用假文件名返回存在 with mock.patch.object(os.path, 'isfile') as mock_isfile: def isfile_side_effect(path): if path in (self.fake_start_log, self.fake_end_log, self.fake_abbr_file): return True return os.path.exists(path) mock_isfile.side_effect = isfile_side_effect # 2. mock open,按文件名返回对应内容,适配按行读取逻辑 with mock.patch("builtins.open", mock.mock_open()) as mock_open_obj: def open_side_effect(path, *args, **kwargs): if path == self.fake_start_log: data = self.start_log_content elif path == self.fake_end_log: data = self.end_log_content elif path == self.fake_abbr_file: data = self.abbr_content else: return open(path, *args, **kwargs) file_obj = mock.mock_open(read_data=data).return_value file_obj.__iter__.side_effect = lambda: iter(data.splitlines()) return file_obj mock_open_obj.side_effect = open_side_effect # 执行断言 result = read_files(self.fake_start_log, self.fake_end_log, self.fake_abbr_file) self.assertEqual(result, self.expected_result) def test_file_not_found_error(self): with self.assertRaises(FileNotFoundError): read_files('a.txt', 'b.txt', 'c.txt') if __name__ == '__main__': unittest.main()
逻辑说明
- 优先mock
os.path.isfile绕过文件存在性校验,仅对测试用的三个假文件名返回存在,不影响其他逻辑 - 通过
side_effect让mock的open方法根据传入的文件名返回对应内容,同时处理多行迭代的适配,保证按行读取的逻辑正常运行 - 不需要创建真实的测试文件,所有逻辑都在mock中完成,符合单元测试隔离性要求
注:如果原函数中
start_lines.pop()是为了去掉文件末尾空行,只需要在self.start_log_content末尾多加一行换行,同步调整期望结果即可。
内容的提问来源于stack exchange,提问作者Gorbatov Nikolay
相关产品推荐
相关产品推荐

