测试中mock pathlib.Path.is_dir()失效的问题排查求助
问题分析与解决方案
我来帮你拆解下问题出在哪,以及怎么修复这个mock失效的问题:
核心问题:mock错了对象
你业务代码里调用的是Path实例的is_dir()方法,但你的测试代码却mock了Path类本身的is_dir属性,两者完全不是一回事:
- 业务代码逻辑:
path = Path(element)→ 创建Path类的实例 → 调用path.is_dir()(实例方法) - 你的测试操作:给
source_code.Path.is_dir赋值了一个MagicMock → 这是给类加了个静态属性,实例调用is_dir()时根本不会用到它
所以你的side_effect完全没生效,调用path.is_dir()得到的是实例默认的mock对象(在Python里mock对象本身是truthy的,所以你的if result判断永远为True)。
另外提一句:你业务代码里还有个小bug,print(f"#MBB SC: {dir} -> {result}")里的dir是Python内置函数,不是你想输出的路径,应该改成element或者path。
正确的测试实现
我们需要让Path类创建的每一个实例,其is_dir()方法按顺序返回True和False。这里推荐用unittest.mock.patch来更优雅地实现:
方案一:逐个定义mock实例
import pytest from unittest.mock import call, patch, MagicMock import source_code class TestSourceCode(): @patch('source_code.Path') def test_simple_method(self, mock_path_class): # 创建两个mock实例,分别设置is_dir的返回值 mock_tmp_instance = MagicMock() mock_tmp_instance.is_dir.return_value = True mock_test_instance = MagicMock() mock_test_instance.is_dir.return_value = False # 让Path类按顺序返回这两个实例 mock_path_class.side_effect = [mock_tmp_instance, mock_test_instance] # 调用业务方法 source_code.simple_method() # 验证Path类的调用是否符合预期 assert mock_path_class.call_args_list == [call('/tmp'), call('/tmp/test')] # 验证每个实例的is_dir方法都被调用了一次 mock_tmp_instance.is_dir.assert_called_once() mock_test_instance.is_dir.assert_called_once()
方案二:用动态函数简化写法
如果不想单独创建每个实例,也可以用一个动态函数来控制返回值:
import pytest from unittest.mock import call, patch import source_code class TestSourceCode(): @patch('source_code.Path') def test_simple_method(self, mock_path_class): # 定义is_dir的返回序列 is_dir_results = [True, False] def mock_is_dir(): return is_dir_results.pop(0) # 让所有Path实例的is_dir方法调用这个动态函数 mock_path_instance = mock_path_class.return_value mock_path_instance.is_dir.side_effect = mock_is_dir source_code.simple_method() # 验证调用次数和参数 assert mock_path_class.call_args_list == [call('/tmp'), call('/tmp/test')] assert mock_path_instance.is_dir.call_count == 2
为什么原来的测试看不到is_dir的调用?
因为你mock的source_code.Path.is_dir是类属性,但业务代码里从来没有调用过Path.is_dir()(类方法),所有调用都是针对实例的is_dir(),所以这个mock的调用计数永远是0,自然看不到调用记录。
内容的提问来源于stack exchange,提问作者MarcnB
相关产品推荐
相关产品推荐

