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_stuff
和 MyClass.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)
我在 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_stuff
和 MyClass.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)