读懂Python的Mock对象库(2)
Mock的常规问题
在你的测试中mock对象可能会引入一些问题。有些问题是mock中固有的,而有的是unittest.mock特有的。记住一点,本教程并未提及其他mock的问题。
这里介绍的问题大多相似,因为它们引起的问题基本相同。在这些情况下,测试断言没有什么作用。虽然每个mock的目标都有校验,但是mock本身并没有。
修改对象接口和拼写
类和函数定义一直在变化。当对象的接口发生改变时,依赖于该对象的任何mock测试都可能会失效。
例如,你重命名了一个方法但是忘记在测试中mock这个方法并且没有调用.assert_not_called()做验证。方法更改后,.assert_not_called()仍然为True,但是断言没有用了,因为方法已经不存在了。
无效的测试看似不重要,但是如果它们是你仅有的测试且你认为它们是正常的,这种情况可能对你的应用程序造成严重影响。
Mock中一个特有的拼写错误问题会导致测试失败。当你访问Mock的成员时,它会创建一个新的接口。所以,如果拼写错误,你可能在无意间创建了一个新的属性。
如果你调用了.asert_called()而不是.assert_called(),测试程序并不会引发AssertionError,这是因为你在Python mock对象上创建了一个名为asert_called()的新方法,而并不是按预计执行了断言。
技术细节:有趣的是,assret是assert的一种特殊拼写错误。如果你尝试访问以assret(或assert)开头的属性,Mock将自动引发AttributeError异常。
当你在自己的代码库中mock对象时会出现这些问题,当你mock的对象与外部交互时,则会出现另外的问题。
修改外部依赖
再设想一下,你的代码向外部API发出请求,这种情况,外部依赖是可以不经你允许而轻易修改的API。
一方面,单元测试是测试代码中单独的某一部分组件。所以,mock代码可以生成请求帮助你在可控条件下测试独立组件。但是它也存在一些潜在问题。
如果外部依赖修改了自身接口,你的Python mock对象将会失效。如果出现了这种情况(并且接口变更被破坏),你的测试会通过,但是生产代码会出错,这是因为你的mock对象覆盖了接口变更。
不幸的是,unittest.mock并没有为这种问题提供解决方案,在mock外部依赖时,你必须自行判断。
以上三种的问题都可能导致测试失效以及潜在的成本问题,因为它们会危及mock的完整性。unittest.mock为处理这些问题提供了一些工具。
利用规范避免常规问题
如上所述,如果修改了对象或者函数定义或者Python mock对象属性拼写错误,会导致测试出现问题。
出现这些问题是由于当你在访问Mock的属性和方法时Mock会创建它们。解决这些问题需要预防Mock创建不符合mock测试预期的对象属性。
配置Mock时,可以将象规范传递给spec参数。spec 接受命名列表或者其他对象,并定义mock的接口。如果你尝试访问不属于该规范的属性,Mock将引发AttributeError异常。
这里,你已经指定calendar拥有名为.is_weekday()和.get_holidays()的方法。当你访问.is_weekday(),它会返回一个Mock对象。当你访问.create_event()时,该方法与规范不匹配,Mock会引发AttributeError异常。
如果使用对象来配置Mock,规范的工作方式也是相同的。
.is_weekday()对于calendar是可用的,因为你配置了calendar来匹配my_calendar模块的接口。
此外,unittest.mock提供了自动指定Mock实例接口的快捷方法。
实现自动化规范的一种方法是create_autospec:
一如前文,calendar是Mock的一个实例,其接口与my_calendar匹配。如果你正在使用patch(),你可以向autospec发送参数以获得相同的结果:
总结
你已经学习了很多关于使用unittest.mock来mock对象的知识!
现在,你可以:
1. 在测试中使用Mock来模拟对象
2. 检查使用数据以便了解如何应用对象
3. 自定义mock对象的返回值和侧面影响
4. 在你的整个代码库中使用patch()为对象打补丁
5. 在使用Python mock对象时了解并避免问题
你已经为更好的理解打好了基础,这可以帮助你更好的构建测试程序。你可以使用mock来深入了解你代码中其他不清楚的地方。
最后给你留下一个忠告,不要过度使使用mock对象!
利用Python mock对象的强大功能并进行mock很简单,但这实际上降低了你测试的价值。
如果你有兴趣了解更多关于unittest.mock的信息,我建议你阅读一下它描述极佳的文档。
英文原文:https://realpython.com/python-mock-library/ 译者:敦伟