Python Mock测试:模拟os.makedirs抛出OSError未生效问题
让我帮你拆解一下测试失败的几个核心原因:
1. 对os.path.dirname的返回值理解有误
当你传入'test'这种纯文件名(不带路径)时,os.path.dirname()返回的是空字符串'',而不是None。虽然你设置mock_os.path.dirname.return_value = None也能让if not dir:条件成立,但更贴合真实场景的应该是返回空字符串。不过这不是测试失败的主要原因,但值得修正。
2. Mock方法抛异常的写法错误
你用了mock_os.makedirs.raiseError.side_effect,这是完全不对的!在unittest.mock的Mock对象里,要让方法抛出异常,直接给方法的side_effect属性赋值异常对象就可以了,正确写法是:
mock_os.makedirs.side_effect = OSError()
你之前的写法相当于给一个不存在的raiseError属性设置side_effect,根本不会对os.makedirs的Mock行为产生影响,所以自然不会抛出异常。
3. 最关键的逻辑误解:原函数已经吞掉了异常
你的原函数里明确写了:
try: os.makedirs(os.path.dirname(dest)) except OSError: pass # 忽略异常
也就是说,就算os.makedirs真的抛出OSError,原函数也会捕获它并直接忽略,不会把异常抛到函数外部。所以你用with self.assertRaises(OSError)去断言函数会抛出异常,从逻辑上就是错误的——这根本不是原函数的设计行为。
修正后的测试代码
如果你的测试目标是验证:当需要创建目录时,函数确实调用了os.makedirs,并且即使该方法抛出异常,函数也能正常执行(不崩溃),可以这么写:
@patch('my.package.os') def test_create_dir_if_not_exists(self, mock_os): # 模拟dirname返回空字符串(对应传入'test'的场景) mock_os.path.dirname.return_value = '' # 让makedirs抛出OSError mock_os.makedirs.side_effect = OSError("测试用异常") # 调用函数,此时内部会捕获异常,不会向外抛出 create_if_not_exists('test') # 验证makedirs确实被调用了一次,参数是空字符串 mock_os.makedirs.assert_called_once_with('')
这个测试的核心是验证函数的行为是否符合预期:尝试创建目录,即使创建失败也不会报错。
另外,你的原函数里还有个小冗余:已经把dir = os.path.dirname(dest)存起来了,后面调用os.makedirs时又重复调用了一次os.path.dirname(dest),可以改成os.makedirs(dir),让代码更高效。
内容的提问来源于stack exchange,提问作者wyt

