选项1: 模拟实例的方法
该模拟库有一个特殊的方法用来装饰模拟对象实例的方法和参数。@mock.patch.object 进行装饰:
#!/usr/bin/env Python# -*- coding: utf-8 -*-from mymodule import RemovalService, UploadServiceimport mockimport unittestclass RemovalServiceTestCase(unittest.TestCase):
@mock.patch('mymodule.os.path')
@mock.patch('mymodule.os')
def test_rm(self, mock_os, mock_path):
# instantiate our service
reference = RemovalService()
# set up the mock
mock_path.isfile.return_value = False
reference.rm("any path")
# test that the remove call was NOT called.
self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
# make the file 'exist'
mock_path.isfile.return_value = True
reference.rm("any path")
mock_os.remove.assert_called_with("any path")
class UploadServiceTestCase(unittest.TestCase):
@mock.patch.object(RemovalService, 'rm')
def test_upload_complete(self, mock_rm):
# build our dependencies
removal_service = RemovalService()
reference = UploadService(removal_service)
# call upload_complete, which should, in turn, call `rm`:
reference.upload_complete("my uploaded file")
# check that it called the rm method of any RemovalService
mock_rm.assert_called_with("my uploaded file")
# check that it called the rm method of _our_ removal_service
removal_service.rm.assert_called_with("my uploaded file")
太棒了!我们验证了上传服务成功调用了实例的rm方法。你是不是注意到这当中有意思的地方了?这种修补机制实际上取代了我们的测试方法的删除服务实例的rm方法。这意味着,我们实际上可以检查该实例本身。如果你想了解更多,可以试着在模拟测试的代码中下断点来更好的认识这种修补机制是如何工作的。
陷阱:装饰的顺序当使用多个装饰方法来装饰测试方法的时候,装饰的顺序很重要,但很容易混乱。基本上,当装饰方法呗映射到带参数的测试方法中时,。比如下面这个例子:
@mock.patch('mymodule.sys')
@mock.patch('mymodule.os')
@mock.patch('mymodule.os.path')
def test_something(self, mock_os_path, mock_os, mock_sys):
pass
注意到了吗,我们的装饰方法的参数是反向匹配的? 这是有部分原因是因为。下面是使用多个装饰方法的时候,实际的代码执行顺序:
patch_sys(patch_os(patch_os_path(test_something)))
由于这个关于sys的补丁在最外层,因此会在最后被执行,使得它成为实际测试方法的最后一个参数。请特别注意这一点,并且在做测试使用调试器来保证正确的参数按照正确的顺序被注入。