Python unittest:无法模拟导入的函数,因此条件计算结果为 False

Python unittest: Unable to mock imported functions so that conditional evaluates to False

我在 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)

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

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

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

然后有一个问题,你需要额外模拟 MyClass.do_other_stuffMyClass.do_something_else 来检查 MyClass 是否根据 return 值调用了正确的下游方法共 functionB.

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

myclass.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

production_class.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

我最后所做的是更改 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)