如何Mock pathlib.Path对象的文件大小属性
Path处理场景下的文件大小测试Mock改造方案
核心改造逻辑
原测试通过mockos.path.getsize获取文件大小,现在代码链路替换为Path(文件路径).stat().st_size,需要按照patch的就近引用原则修改mock目标,模拟完整的方法调用链返回值,不需要触发真实的文件系统IO。
关键注意点:st_size并不是Path类自带的属性,是stat()方法执行完成后,返回的文件状态对象的属性,因此不能直接对st_size本身打补丁,需要逐层模拟调用链的返回值。
具体代码修改步骤
- 首先确认业务模块(也就是原代码里的
su)对Path的导入方式,确定patch目标:- 如果
su模块里写了from pathlib import Path,代码里直接用Path(...),patch目标就是su.Path - 如果
su模块里写了import pathlib,代码里用pathlib.Path(...),patch目标就是su.pathlib.Path
- 如果
- 在with块中配置mock的返回值:让被mock的Path类实例,在调用
.stat()方法时,返回一个带预设st_size属性的对象。
改造后的测试代码示例
把原来的测试逻辑对应替换后,完整代码如下:
# init test values test_value = 7.5 size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") # mock 0 B file with patch('su.Path') as mock_path: # 逐层配置调用链返回值 mock_path.return_value.stat.return_value.st_size = 0 txt = su.get_file_size('random') self.assertEqual(txt, '0B') # mock every pow of 1024 to YB for i in range(9): with patch('su.Path') as mock_path: mock_path.return_value.stat.return_value.st_size = test_value*(1024**i) txt = su.get_file_size('random') self.assertEqual(txt, f'7.5 {size_name[i]}')
常用优化写法
可以把重复的mock配置抽成通用工具函数,减少重复代码:
from contextlib import contextmanager @contextmanager def mock_file_size(size: int): with patch('su.Path') as mock_path: mock_path.return_value.stat.return_value.st_size = size yield # 测试时直接调用 with mock_file_size(0): txt = su.get_file_size('random') self.assertEqual(txt, '0B') for i in range(9): with mock_file_size(test_value*(1024**i)): txt = su.get_file_size('random') self.assertEqual(txt, f'7.5 {size_name[i]}')
常见踩坑提醒
- 不要把patch目标设为
pathlib.Path:这是pathlib模块里原生的Path定义位置,按照mock规则,只有当代码运行时引用的是这个位置的对象,mock才会生效,多数业务代码导入Path后,引用已经指向自己模块的命名空间,打在原生位置的补丁不会被业务代码捕获 - 不需要模拟Path的其他行为:测试只关注
st_size返回值对应的换算逻辑,Path初始化、stat的其他返回属性完全不需要处理,不影响测试结果
内容的提问来源于stack exchange,提问作者Pierrick Rambaud
相关产品推荐
相关产品推荐

