@ mock.patch如何知道每个模拟对象使用哪个参数?

时间:2015-10-26 19:17:54

标签: python unit-testing mocking

查看此网页:http://www.toptal.com/python/an-introduction-to-mocking-in-python - 作者在Python中讨论了Mocking和Patching,并提供了一个非常可靠的真实世界"例。绊倒我的部分是了解单元测试框架如何工作知道哪个模拟对象传递给哪个补丁。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import os.path

def rm(filename):
    if os.path.isfile(filename):
        os.remove(filename)

代码示例非常容易理解。对OS库/模块的硬编码依赖性。首先使用os.path.isfile()方法检查文件是否存在,如果是,请使用os.remove()将其删除

测试/模拟代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from mymodule import rm

import mock
import unittest

class RmTestCase(unittest.TestCase):

    @mock.patch('mymodule.os.path')
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os, mock_path):
        # set up the mock
        mock_path.isfile.return_value = False

        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

        rm("any path")

        mock_os.remove.assert_called_with("any path")

我想让我感到困惑的是,在测试中传递了2个@Patch调用和2个参数。单元测试框架如何知道mymodule.os.path正在修补os.path并且它已映射到mock_pathmymodule.os.path定义在哪里?

(似乎有很多"魔法"正在进行,而且我没有关注它。)

2 个答案:

答案 0 :(得分:3)

它按照装饰器的执行顺序进行,也是传递给测试方法的参数的顺序......

装饰器执行的顺序如下所示: https://thadeusb.com/weblog/2010/08/23/python_multiple_decorators/

当您按照编写方式使用补丁时,会自动为您创建一个Mock实例,并将其作为参数传递给您的测试方法。还有另一个版本:

@mock.patch("subprocess.check_output", mock.MagicMock(return_value='True'))
def test_mockCheckOutput(self):
    self.assertTrue(subprocess.check_output(args=[])=='True')

在这种情况下,你传递自己的Mock对象,在这个例子中,当你调用subprocess.check_output()时,它将返回'True'

但是,你可以这样做:

def test_mockCheckOutput(self):
    m = mock.MagicMock(return_value='True')
    with mock.patch("subprocess.check_output", m):
        self.assertTrue(subprocess.check_output(args=[])=='True')

并且在这种情况下,您可以传递任何您想要的模拟项目,因为它将在运行时期间进行评估...:)

答案 1 :(得分:2)

当应用装饰器时,最好像这样看

<wrapper1>
    <wrapper2>
        <wrapper3>
           **Your Function**
        </wrapper3>
    </wrapper2>
</wrapper1>

基本上,您的函数需要按此顺序与包装器进行交互:

wrapper3 - &GT; wrapper2 - &GT; wrapper1

@wrapper1
@wrapper2
@wrapper3
def your_func(wrapper1.input, wrapper2.input, wrapper3.input):

注意 wrapper1.input不是你实际引用其输入的方式

要回答问题的第二部分,mymodule.os如何知道引用操作系统。修补时,您实际上正在拦截对该特定名称的调用。当您在os中致电mymodule时,您实际上正在呼叫mymodule.os。在修补时,您必须引用通过实际代码中调用它的方式来模拟的类,而不是从测试模块的角度来看