Python unittest:到mock.patch()或只是用Mock替换方法?

时间:2014-09-28 16:22:22

标签: python unit-testing mocking decorator

在Python中编写单元测试时模拟类或方法时,为什么需要使用@patch装饰器?我只是可以用Mock对象替换方法而不需要任何补丁注释。

示例:

class TestFoobar(unittest.TestCase):
def setUp(self):
    self.foobar = FooBar()

# 1) With patch decorator:

@patch.object(FooBar, "_get_bar")
@patch.object(FooBar, "_get_foo")
def test_get_foobar_with_patch(self, mock_get_foo, mock_get_bar):
    mock_get_bar.return_value = "bar1"
    mock_get_foo.return_value = "foo1"

    actual = self.foobar.get_foobar()

    self.assertEqual("foo1bar1", actual)

# 2) Just replacing the real methods with Mock with proper return_value:

def test_get_foobar_with_replacement(self):
    self.foobar._get_foo = Mock(return_value="foo2")
    self.foobar._get_bar = Mock(return_value="bar2")

    actual = self.foobar.get_foobar()

    self.assertEqual("foo2bar2", actual)

有人可以提供一个示例,其中补丁装饰器是好的,替换是坏的吗?

我们总是在我们的团队中使用补丁装饰器,但在阅读了this comment的帖子之后,我知道也许我们可以编写更好看的代码,而不需要补丁装饰器。

我知道修补是暂时的,所以也许在某些情况下,不使用补丁装饰器并用mock替换方法是危险的?可能是在一个测试方法中替换对象会影响下一个测试方法的结果吗?

我试图证明这一点,但是空洞了:两个测试都在下一个代码中传递:

def test_get_foobar_with_replacement(self):
    self.foobar._get_foo = Mock(return_value="foo2")
    self.foobar._get_bar = Mock(return_value="bar2")

    actual = self.foobar.get_foobar()

    self.assertIsInstance(self.foobar._get_bar, Mock)
    self.assertIsInstance(self.foobar._get_foo, Mock)
    self.assertEqual("foo2bar2", actual)

def test_get_foobar_with_real_methods(self):

    actual = self.foobar.get_foobar()

    self.assertNotIsInstance(self.foobar._get_bar, Mock)
    self.assertNotIsInstance(self.foobar._get_foo, Mock)
    self.assertIsInstance(self.foobar._get_bar, types.MethodType)
    self.assertIsInstance(self.foobar._get_foo, types.MethodType)
    self.assertEqual("foobar", actual)

完整源代码(Python 3.3):dropbox.com/s/t8bewsdaalzrgke/test_foobar.py?dl=0

1 个答案:

答案 0 :(得分:5)

在测试方法返回后,

patch.object会将您修补的项目恢复到其原始状态。如果您自己修补对象,则需要恢复原始值,如果该对象将在另一个测试中使用。

在你的两个例子中,你实际上修补了两个不同的东西。您对patch.object的调用会修补 FooBar,而您的猴子修补程序会修补FooBar的特定实例

如果每次都从头开始创建对象,则恢复原始对象并不重要。 (您不会显示它,但我认为self.foobar方法是使用setUp方法创建的,因此即使您替换其_get_foo方法,也不会在多次测试中重用特定的对象。)