Python单元测试:无法模拟导入的函数,从而使条件求值为False



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

以下是我想要测试的代码:

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时,变量没有设置为我的返回值;它被设置为一个MagicMock对象(因此if语句的求值结果总是为True(。如何模拟导入的函数,以便在执行时,变量实际上被设置为返回值,而不是MagicMock对象本身?

我们必须了解path.to.MyClass.functionB实际使用的导入路径。当模拟对象时,不一定直接使用对象所在的路径,而是在递归导入模块时,interpreter看到的路径

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

还有一个问题是,您需要在中添加MyClass.do_other_stuffMyClass.do_something_else的mock,以检查MyClass是否根据functionB的返回值调用了正确的下游方法。

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

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)

相关内容

  • 没有找到相关文章

最新更新