如何正确模拟 类 和导入模块中的方法
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_one
当var_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_1
和 Class_2
,您紧密耦合了这三段代码,但现在您想单独测试它们。这将很难,并且可能会导致脆弱且难以阅读的测试。
如果您真的希望您的代码像那样,我建议您在不模拟 Class_1
和 Class_2
的情况下测试该方法。现在,如果您说您不想这样做,因为 stuff
(或更好,stuff.data
;当您只需要数据时,为什么要传递整个想法?)很难模拟或实例化,我猜你的设计有问题,但这是我从你设计的例子中所能得到的。
我正在尝试为需要使用 类 从我不想使用的外部模块实例化某些对象的方法创建一些单一测试,因为它们需要我不能使用的参数在对象初始化时传递。
例如,假设我要测试的代码具有以下结构:
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_one
当var_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_1
和 Class_2
,您紧密耦合了这三段代码,但现在您想单独测试它们。这将很难,并且可能会导致脆弱且难以阅读的测试。
如果您真的希望您的代码像那样,我建议您在不模拟 Class_1
和 Class_2
的情况下测试该方法。现在,如果您说您不想这样做,因为 stuff
(或更好,stuff.data
;当您只需要数据时,为什么要传递整个想法?)很难模拟或实例化,我猜你的设计有问题,但这是我从你设计的例子中所能得到的。