【问题标题】:How to mock properly classes and methods from imported modules如何从导入的模块中正确模拟类和方法
【发布时间】:2019-06-03 16:43:59
【问题描述】:

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

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

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.mockpatch(但它不起作用):

@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.data 用作 var_oneClass2 的参数。

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

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

【问题讨论】:

  • 我认为你不应该设置mocked_class1.return_value。无论如何,您想在这里测试什么?你没有做出任何断言。
  • 用断言编辑。我想检查var_two.attributewhat_i_want_to_get。问题不在于mocked_class1,而在于Class2,这似乎需要我没有的论据,即使我使用patch。正如我所说,我想要的是在将任何参数传递给Class2 时始终获得相同的值,即使我在method_to_be_tested 中使用的参数不存在。我的意思是,我在测试中真正想要的是直接将我想要的值分配给var_two。我不知道这是否可能。
  • 什么是mock?有一个模块 unittest.mock 但当然它是不可调用的,所以 stuff = mock() 引发 TypeError: 'module' object is not callable 或只是 NameError: name 'mock' is not defined 对我来说。
  • "str 对象没有属性数据,指的是 var_one" 当然,你有 `var_one = Class_1(stuff.data)` 并且你正在修补 Class_1 以返回一个字符串,所以 @ 987654348@ 变成一个字符串,它没有data 属性。
  • mock 是从 mockito 模块导入的(对不起,我应该指定它)。即使var_one 没有预期的属性,有什么方法可以让Class2(var_one.data) 返回我想要的吗?

标签: python python-unittest


【解决方案1】:

你当然可以不用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;为什么在只需要数据的时候还要考虑整个想法?)很难模拟或实例化,我猜你的设计有问题,但这是我从你人为的例子中所能得到的。

【讨论】:

  • 谢谢。我意识到问题出在从其他模块导入的混乱中。也感谢您的建议,但这是我试图简化的测试的一部分(也许我让它更难理解),所以你的回答可以满足我的需要?
猜你喜欢
  • 2021-04-04
  • 1970-01-01
  • 2018-03-28
  • 1970-01-01
  • 1970-01-01
  • 2020-01-10
  • 2020-03-15
  • 2021-03-13
相关资源
最近更新 更多