【问题标题】:Python unittest: to mock.patch() or just replace method with Mock?Python unittest:要 mock.patch() 还是用 Mock 替换方法?
【发布时间】:2014-09-28 16:22:22
【问题描述】:

在 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 的帖子后,我想到也许我们可以编写更好看的代码而不需要补丁装饰器。

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

我试图证明这一点,但结果是空的:两个测试都通过了下一个代码:

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

【问题讨论】:

    标签: python unit-testing mocking decorator


    【解决方案1】:

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

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

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

    【讨论】:

    • 我以为我只修补了方法,而不是类。我读了this,据我所知,我修补了方法,而不是类。你能告诉我如何做得更好吗?
    • “如果每次都从头开始创建对象,那么恢复原始对象并不重要” - 我理解这意味着可以只用 Mock() 替换方法而不使用补丁只要您在 setUp 方法中创建可重用对象。我做了一个例子,第二个测试得到断言错误 - 测试期望该方法不是 Mock 的实例,但它是,因为 foobar 对象是在测试类之外创建的,而不是在 setUp 方法中:dropbox.com/s/p1djmm9bv7op25q/test_foobar2.py?dl=0
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多