如何使用Python unittest测试仅打开特定文件名模式文件的函数
如何使用Python unittest测试仅打开特定文件名模式文件的函数
我来帮你搞定这个单元测试的问题!要验证你的get_tests()函数只处理符合test_*.json模式的文件,我们可以通过Mockglob.iglob和open这两个关键点来实现——完全不用依赖真实的文件系统,测试既可靠又快速。
核心思路
我们的测试要做两件事:
- 模拟
glob.iglob返回混合的文件名列表(包含符合test_*.json的和不符合的) - 追踪
open函数的调用情况,最后断言只有符合模式的文件被实际打开了
这样就能精准验证函数的过滤逻辑是否符合预期,不会误打开其他文件。
完整测试代码示例
假设你的get_tests函数放在my_module.py里,测试代码可以这么写:
import unittest from unittest import mock from my_module import get_tests # 导入要测试的函数 class TestGetTests(unittest.TestCase): def test_only_opens_test_json_files(self): # 1. 定义模拟的文件名列表:包含符合的和不符合的 mock_file_paths = [ "test_login.json", "subdir/test_checkout.json", "random.txt", "config.json", "test_data.xml" ] # 2. Mock glob.iglob,让它返回我们定义的文件名 with mock.patch('glob.iglob', return_value=mock_file_paths): # 3. Mock open函数,同时模拟返回合法的JSON内容(避免json.loads报错) mock_file_content = '{"id": 1, "name": "test"}' with mock.patch('builtins.open', mock.mock_open(read_data=mock_file_content)) as mock_open: # 4. 调用要测试的函数 result = get_tests() # 5. 断言:只有符合test_*.json的文件被打开 expected_calls = [ mock.call("test_login.json"), mock.call("subdir/test_checkout.json") ] mock_open.assert_has_calls(expected_calls, any_order=True) # 额外验证:返回的结果数量和符合条件的文件数量一致 self.assertEqual(len(result), 2) def test_returns_empty_list_when_no_matching_files(self): # 模拟glob返回空列表或者全是不符合的文件 with mock.patch('glob.iglob', return_value=["not_test.json", "readme.md"]): with mock.patch('builtins.open', mock.mock_open()): result = get_tests() # 断言open从未被调用 mock.patch('builtins.open').assert_not_called() # 断言返回空列表 self.assertEqual(result, []) if __name__ == '__main__': unittest.main()
关键细节说明
Mock
glob.iglob的姿势
我们直接让它返回预设的文件名列表,这样就能精准控制测试用的输入——不管真实文件系统里有什么,测试都只认我们给的这些文件名。Mock
open的注意点
因为你的函数里用到了json.loads,所以必须让mock的open返回合法的JSON字符串,不然会抛出解析错误,打断测试流程。我们用mock.mock_open(read_data=...)来模拟文件内容。验证调用的技巧
assert_has_calls用来检查open被调用的所有参数,any_order=True允许调用顺序和我们预设的不一致(因为glob.iglob的递归遍历顺序可能有变化,但这不影响结果)assert_not_called用来验证当没有符合条件的文件时,open完全不会被触发
关于Patch的目标路径
如果你要测试的函数在自己的模块里(比如my_module.py),要注意Mock的路径要和函数里实际引用的模块一致。比如函数里用的是glob.iglob,那我们就Patchglob.iglob;如果函数里是from glob import iglob,那就要Patchmy_module.iglob,这点别搞错!
备注:内容来源于stack exchange,提问作者RoyalSwish
相关产品推荐
相关产品推荐

