如何正确模拟 类 和导入模块中的方法

How to mock properly classes and methods from imported modules

我正在尝试为需要使用 类 从我不想使用的外部模块实例化某些对象的方法创建一些单一测试,因为它们需要我不能使用的参数在对象初始化时传递。

例如,假设我要测试的代码具有以下结构:

from module_one import Class_1
from module_two import Class_2

class MyMainClass()
    def method_to_be_tested(self, stuff):
        var_one = Class_1(stuff.data)
        self.var_two = Class_2(var_one.data)
        print("The var is %s" % self.var_two.attribute)

stuff 是一个复杂的参数,有几个我无法模拟的属性。

这是我在我的测试方法中尝试过的,使用 unittest.mock 中的 patch (但它没有用):

@patch('module_two.Class_2')
@patch('module_one.Class_1')
def test_method(self, mocked_class1, mocked_class2):
    stuff = mock()
    mocked_class1.return_value = 'some_stuff_that_i_dont_want'
    mock_class2 = mock()
    mock_class2.attribute = 'what_i_want_to_get'
    mocked_class2 = mock_class2
    mymainclass.method_to_be_tested(stuff)
    assertEqual('what_i_want_to_get', mymainclass.var_two.attribute)

似乎是补丁什么的没有用,因为它抛出一个错误告诉我 str 对象没有属性数据,指的是 var_onevar_one.data 用作 Class2 的参数。

我想要的是将任何参数传递给 Class2 并始终获得我在 mock_class2 中定义的内容。

编辑: mock() 是从 mockito 模块导入的,但也许我不需要这个来实现我需要的。

你当然可以不用 mockito 做到这一点。假设 MyMainClass 定义在模块 mainmodule:

import unittest.mock as mock


class TestMyMainClass(unittest.TestCase):
    @mock.patch('mainmodule.Class_2')
    @mock.patch('mainmodule.Class_1')
    def test_method(self, mocked_class1, mocked_class2):
        stuff = mock.Mock()
        mocked_class2_instance = mock.Mock()
        mocked_class2_instance.attribute = 'what_i_want_to_get'
        mocked_class2.return_value = mocked_class2_instance
        mymainclass = MyMainClass()
        mymainclass.method_to_be_tested(stuff)
        self.assertEqual('what_i_want_to_get', mymainclass.var_two.attribute)


if __name__ == '__main__':
    unittest.main()

现在如果你认为这个测试很糟糕,我必须同意。

  • 你必须打补丁 Class_1,但它甚至没有出现在测试中。
  • 除非您熟悉被测方法的内部实现细节,否则 Class_2 的相关性并不明显。实际上,您正在测试实现细节而不是(或除了)可观察到的行为,这通常是不好的。
  • 该方法的行为(希望)取决于 stuff.data 但测试会忽略它并且会很高兴地通过它。但是您需要一个具有 data 属性的 stuff 对象,即使您不关心它是什么。

这里的主要问题是,通过在方法内部实例化 Class_1Class_2,您紧密耦合了这三段代码,但现在您想单独测试它们。这将很难,并且可能会导致脆弱且难以阅读的测试。

如果您真的希望您的代码像那样,我建议您在不模拟 Class_1Class_2 的情况下测试该方法。现在,如果您说您不想这样做,因为 stuff(或更好,stuff.data;当您只需要数据时,为什么要传递整个想法?)很难模拟或实例化,我猜你的设计有问题,但这是我从你设计的例子中所能得到的。