Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 模拟对象的单元测试方法_Python_Unit Testing_Mocking - Fatal编程技术网

Python 模拟对象的单元测试方法

Python 模拟对象的单元测试方法,python,unit-testing,mocking,Python,Unit Testing,Mocking,我试图掌握模仿对象的窍门,似乎被一些非常基本的东西弄糊涂了。我试图模拟对象MyClass,然后对其中一个方法进行单元测试。这是我的密码: import mock import unittest class MyClass(object): def __init__(self, a): self.a = a def add_two(self): return self.a + 2 class TestMyClass(unittest.TestCa

我试图掌握模仿对象的窍门,似乎被一些非常基本的东西弄糊涂了。我试图模拟对象MyClass,然后对其中一个方法进行单元测试。这是我的密码:

import mock
import unittest

class MyClass(object):
    def __init__(self, a):
        self.a = a
    def add_two(self):
        return self.a + 2

class TestMyClass(unittest.TestCase):
    @mock.patch('__main__.MyClass')
    def test_add_two(self, dummy_mock):
        m_my_class = mock.Mock()
        m_my_class.a = 10
        result = m_my_class.add_two() # I would expect the result to be 12
        import ipdb;ipdb.set_trace()
        self.assert_equal(result, 12)

if __name__  == '__main__':
    unittest.main()
m_my_class.a=10
中,我将
a
的值设置为,然后在
m_my_class.add_two()
中我添加了一个2,我不应该得到12吗?但是,
结果是:

     16         import ipdb;ipdb.set_trace()
---> 17         self.assert_equal(result, 12)
     18 

ipdb> result
<Mock name='mock.add_two()' id='18379792'>
结果:

ipdb> result
<MagicMock name='MyClass.add_two()' id='38647312'>
ipdb>结果

在这里,修补类几乎肯定不是您想要做的事情。例如,如果您将测试方法更改为:

class TestMyClass(unittest.TestCase):
    @mock.patch('__main__.MyClass')
    def test_add_two(self, dummy_mock):
        m_my_class = MyClass(5)
        print m_my_class
你会发现,不是得到你所期望的:

<__main__.MyClass object at 0x0000000002C53E48>
通常情况下,您不必为
self
参数传入参数,但在这里,我们提取了您确实需要传入
self
参数的函数。如果需要,可以将函数转换为绑定到模拟对象的方法,如下所示:

import types
...
    test_mock.add_two = types.MethodType(MyClass.add_two.__func__, test_mock, test_mock.__class__)
    result = test_mock.add_two()
如果您正在测试的函数调用任何其他方法,那么您需要为每个方法执行此操作或模拟它


我建议不要过多地使用这种策略,部分原因是需要注意被测试方法调用的方法。当然,能够模仿它所依赖的方法可能正是你想要做的。无论如何,它要求单元测试对方法的工作原理有相当深刻的理解,而不仅仅是它应该产生什么。这使得测试非常脆弱,因此您可能会比正在测试的方法更频繁地看到测试中断。

修补类几乎肯定不是您希望在这里做的事情。例如,如果您将测试方法更改为:

class TestMyClass(unittest.TestCase):
    @mock.patch('__main__.MyClass')
    def test_add_two(self, dummy_mock):
        m_my_class = MyClass(5)
        print m_my_class
你会发现,不是得到你所期望的:

<__main__.MyClass object at 0x0000000002C53E48>
通常情况下,您不必为
self
参数传入参数,但在这里,我们提取了您确实需要传入
self
参数的函数。如果需要,可以将函数转换为绑定到模拟对象的方法,如下所示:

import types
...
    test_mock.add_two = types.MethodType(MyClass.add_two.__func__, test_mock, test_mock.__class__)
    result = test_mock.add_two()
如果您正在测试的函数调用任何其他方法,那么您需要为每个方法执行此操作或模拟它


我建议不要过多地使用这种策略,部分原因是需要注意被测试方法调用的方法。当然,能够模仿它所依赖的方法可能正是你想要做的。无论如何,它要求单元测试对方法的工作原理有相当深刻的理解,而不仅仅是它应该产生什么。这使得测试非常脆弱,因此您可能会比您正在测试的方法更频繁地看到测试中断。

为什么您要嘲笑您的SUT?这通常是你应该避免做的事情,因为这会带来你不会测试你认为你正在测试的东西的风险

单元测试的目的是验证完全隔离的单元的行为。一个单元通常与其他单元协作以提供一些有用的功能。(模拟、伪造等)是用于实现这种隔离的工具。在单元测试中,SUT的合作者被替换为测试替身,以便进行测试

您的SUT,
MyClass
,似乎没有任何协作者。因此,不需要双重测试来单独测试这个单元(它已经是独立的)。这使您可以大大简化单元测试:

import mock
import unittest

class MyClass(object):
    def __init__(self, a):
        self.a = a
    def add_two(self):
        return self.a + 2

class TestMyClass(unittest.TestCase):
    def test_add_two(self):
        m_my_class = MyClass()
        m_my_class.a = 10
        result = m_my_class.add_two() # I would expect the result to be 12
        import ipdb;ipdb.set_trace()
        self.assert_equal(result, 12)

if __name__  == '__main__':
    unittest.main()
编辑:以下代码演示了如何使用模拟对象。(免责声明:我通常不在Python中工作,所以我的代码可能是。不过,希望核心点仍然有意义。)

在本例中,
MyClass
添加了由合作者提供的值,而不是核心值(2)

上面的示例使用模拟对象,而不是修补类。以下是对差异的一个很好的解释:


你为什么要嘲笑你的SUT?这通常是你应该避免做的事情,因为这会带来你不会测试你认为你正在测试的东西的风险

单元测试的目的是验证完全隔离的单元的行为。一个单元通常与其他单元协作以提供一些有用的功能。(模拟、伪造等)是用于实现这种隔离的工具。在单元测试中,SUT的合作者被替换为测试替身,以便进行测试

您的SUT,
MyClass
,似乎没有任何协作者。因此,不需要双重测试来单独测试这个单元(它已经是独立的)。这使您可以大大简化单元测试:

import mock
import unittest

class MyClass(object):
    def __init__(self, a):
        self.a = a
    def add_two(self):
        return self.a + 2

class TestMyClass(unittest.TestCase):
    def test_add_two(self):
        m_my_class = MyClass()
        m_my_class.a = 10
        result = m_my_class.add_two() # I would expect the result to be 12
        import ipdb;ipdb.set_trace()
        self.assert_equal(result, 12)

if __name__  == '__main__':
    unittest.main()
编辑:以下代码演示了如何使用模拟对象。(免责声明:我通常不在Python中工作,所以我的代码可能是。不过,希望核心点仍然有意义。)

在本例中,
MyClass
添加了由合作者提供的值,而不是核心值(2)

上面的示例使用模拟对象,而不是修补类。以下是对差异的一个很好的解释:


我认为我不理解第一部分。如果不是这样:
result=m_my_class.add_two()
我运行
result=m_my_class.没有这样的方法()
,我会得到相同的精确结果,因此似乎
add_two
MyClass
的方法这一事实在这里并不相关。@Akavall很抱歉,我在你的代码中遗漏了一个导入细节。我已经更新了我的答案。谢谢,但即使我使用
dummy\u mock
,我还是得到了同样的答案。我更新了问题,以显示我正在使用的代码。@Akavall我比我想象中的mock更粗糙了一点。我提醒自己到底发生了什么,并再次更新了我的答案。@Akavall我发现了如何单独测试一个方法,这正是您在示例中试图做的。我已将其添加到我的answ中