单元测试patch os.path.join覆盖测试文件自身调用该如何解决
核心原因
所有Python运行时中,标准库os是全局单例的模块实例,你当前的补丁写法@patch('datamanager.builddatahelpers.os.path.join')本质是直接修改了全局os模块的path.join属性,因此所有导入os并调用os.path.join的代码都会受到补丁影响,自然也包括测试文件内的调用。
可行解决方案
方案1:调整被测代码导入规则(符合mock最佳实践)
在被测的builddatahelpers.py中将需要mock的方法单独导入到模块自身的命名空间,这样补丁只会作用于该模块内的独立引用,不会影响全局os模块:
修改后的datamanager/builddatahelpers.py:
import os from os.path import join as os_path_join PATH = 'not/test/data/either' class BuilddataHelpers: def create_folder(self, folder_name): create_folder_path = os_path_join(PATH, folder_name) os.makedirs(create_folder_path, exist_ok = True) return create_folder_path # 修正原代码中返回未定义变量`path`的错误
对应修改测试代码的补丁目标:
@patch('datamanager.builddatahelpers.os_path_join') @patch('datamanager.builddatahelpers.os.makedirs') # 新增对makedirs的mock避免真实创建文件夹 def test_create_folder_structure_on_ftp(self, mock_makedirs, mock_os_join): folder_name = 'test_folder' expected_path = os.path.join(self.TEST_PATH, folder_name) mock_os_join.return_value = expected_path self.dm.create_folder(folder_name) # 此处测试文件的os.path.join完全不受补丁影响 self.assertTrue(os.path.exists(os.path.join(self.TEST_PATH, folder_name))) print('SUBFOLDER PATH: ' , os.path.join('not', 'test', 'data')) # 可选:验证调用符合预期 mock_os_join.assert_called_once_with(PATH, folder_name) mock_makedirs.assert_called_once_with(expected_path, exist_ok=True)
方案2:不修改被测代码,测试侧保留原生方法
如果不想改动被测代码,可以在补丁生效前提前保存原生的os.path.join方法,测试过程中使用保存的原生方法即可:
修改后的测试代码示例:
import os import unittest from unittest.mock import patch from datamanager.datamanager import DataManager class TestBuilddataHelpers(unittest.TestCase): TEST_PATH = 'tests/data' def setUp(self): super().setUp() self.dm = DataManager() @patch('datamanager.builddatahelpers.os.path.join') @patch('datamanager.builddatahelpers.os.makedirs') def test_create_folder_structure_on_ftp(self, mock_makedirs, mock_os_join): # 补丁生效前先保存原生join方法 real_join = os.path.join folder_name = 'test_folder' expected_path = real_join(self.TEST_PATH, folder_name) mock_os_join.return_value = expected_path self.dm.create_folder(folder_name) # 使用不受补丁影响的原生join执行操作 self.assertTrue(os.path.exists(real_join(self.TEST_PATH, folder_name))) print('SUBFOLDER PATH: ' , real_join('not', 'test', 'data'))
额外说明
你原测试逻辑中os.makedirs没有被mock,所以会真实在本地创建文件夹,如果是单元测试场景建议同时mock该方法避免修改本地文件系统。
内容的提问来源于stack exchange,提问作者smbfml
相关产品推荐
相关产品推荐

