0

我在 Python 中遇到单元测试问题。具体来说,当我尝试模拟我的代码导入的函数时,分配给该函数输出的变量被分配给一个 MagicMock 对象,而不是模拟函数的 return_value。我一直在研究 python 的 unittest 库的文档,但没有任何运气。

以下是我要测试的代码:

from production_class import function_A, function_B, function_M

class MyClass:
    def do_something(self):
        variable = functionB()
        if variable:
            do_other_stuff()
        else:
            do_something_else

这是我尝试过的:

@mock.patch(path.to.MyClass.functionB)
@mock.patch(<other dependencies in MyClass>)
def test_do_something(self, functionB_mock):
    functionB_mock.return_value = None # or False, or 'foo' or whatever.
    myClass = MyClass()
    myClass.do_something()
    self.assertTrue(else_block_was_executed)

我遇到的问题是,当测试进入variable = functionBMyClass 时,变量没有设置为我的返回值;它被设置为一个 MagicMock 对象(因此 if 语句总是计算为 True)。如何模拟导入的函数,以便在执行时变量实际上设置为返回值而不是 MagicMock 对象本身?

4

2 回答 2

2

我们必须查看您实际使用的导入路径path.to.MyClass.functionB。在模拟对象时,您不必直接使用指向对象所在位置的路径,而是使用递归导入模块时解释器看到的路径。

例如,如果您的测试MyClass从导入myclass.py,并且该文件functionB从导入production_class.py,则模拟路径将是myclass.functionB,而不是production_class.functionB.

然后是你需要额外的 mockMyClass.do_other_stuffMyClass.do_something_elsein 来检查是否MyClass调用了正确的下游方法,基于functionB.

这是一个工作示例,用于测试 的可能返回值functionB,以及它们是否调用正确的下游方法:

我的类.py

from production_class import functionA, functionB, functionM


class MyClass:
    def do_something(self):
        variable = functionB()
        if variable:
            self.do_other_stuff()
        else:
            self.do_something_else()

    def do_other_stuff(self):
        pass

    def do_something_else(self):
        pass

生产类.py

import random

def functionA():
    pass

def functionB():
    return random.choice([True, False])

def functionM():
    pass

test_myclass.py

import unittest
from unittest.mock import patch
from myclass import MyClass


class MyTest(unittest.TestCase):

    @patch('myclass.functionB')
    @patch('myclass.MyClass.do_something_else')
    def test_do_something_calls_do_something_else(self, do_something_else_mock, functionB_mock):
        functionB_mock.return_value = False
        instance = MyClass()
        instance.do_something()
        do_something_else_mock.assert_called()


    @patch('myclass.functionB')
    @patch('myclass.MyClass.do_other_stuff')
    def test_do_something_calls_do_other_stuff(self, do_other_stuff_mock, functionB_mock):
        functionB_mock.return_value = True
        instance = MyClass()
        instance.do_something()
        do_other_stuff_mock.assert_called()


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

调用python test_myclass.py结果:

..
----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK
于 2020-04-10T17:43:58.687 回答
0

我最终做的是更改 MyClass 中的导入语句以导入对象而不是单个方法。然后我可以毫无问题地模拟该对象。

更明确地说,我将 MyClass 更改为如下所示:

import production_class as production_class

class MyClass:
    def do_something(self):
        variable = production_class.functionB()
        if variable:
            do_other_stuff()
        else:
            do_something_else

并将我的测试更改为

@mock.patch(path.to.MyClass.production_class)
def test_do_something(self, prod_class_mock):
    prod_class_mock.functionB.return_value = None
    myClass = MyClass()
    myClass.do_something()
    self.assertTrue(else_block_was_executed)
于 2020-04-13T17:03:39.350 回答