我正在尝试模拟实用程序类的几个组件。尽管assert_called()
对于一种方法而言是可以的,但对于另一种方法而言却是失败的,但是我敢肯定两者都可以被调用。我正在Windows 10上运行Python 3.7.3。
我已经将方案简化为基本要素。
实用程序类(util.py
):
class api:
@staticmethod
def send(data):
print("sending %s" % data)
class logger:
@staticmethod
def info(s):
print("INFO: %s" % s)
@staticmethod
def error(s):
print("ERROR: %s" % s)
正常运行的单元测试变体:
import unittest
from unittest.mock import patch
from util import api
def do_something():
api.logger.info("doing something")
api.send("some data")
@patch("util.api.logger")
class Test(unittest.TestCase):
def test_do_something(self, mock_logger):
do_something()
mock_logger.info.assert_called()
失败的一个:
import unittest
from unittest.mock import patch
from util import api
def do_something():
api.logger.info("doing something")
api.send("some data")
@patch("util.api")
class Test(unittest.TestCase):
def test_do_something(self, mock_api):
do_something()
mock_api.send.assert_called()
运行测试时,我同时获得两个print()
输出:
INFO: doing something
sending some data
所以我确定两种方法都被调用。
很可能我犯了一些愚蠢的虚拟错误,因为我真的是python新手...
更多背景:
在精简方案中,do_something()
只是一组功能的替代,这些功能是我测试的对象,而在我的实际方案中,这些功能实际上是在单独的python文件中定义的。在生产环境中,它在提供实用程序类api的框架的上下文中运行。在我的测试环境中,util.py
本身是生产api的模拟。像这样加载要测试的py文件(而不是def do_something(): ...
):
path = os.getcwd() + "<local path to py file to be tested>"
globals().update({ **runpy.run_path(path, init_globals=globals()), **globals() })
因此,我无法在测试方案中修改do_something()
代码。
答案 0 :(得分:2)
这是一个棘手的问题。
问题是测试类在修补之前就已经导入了api
类。来自patch的文档:
目标在执行修饰函数时而不是在修饰时导入。
由于首先在测试类中导入了api
类,所以该人在运行测试时不会被打补丁。 Python认为它已经被导入。
请注意,logger
类不是这种情况,因为您从不会导入它。如果有的话,第一个测试也会失败。
最简单的解决方法-将导入移动到测试功能中。
def do_something():
from util import api
api.logger.info("doing something")
api.send("some data")
现在patch
将执行导入-因此该模拟将放置在测试范围中,而不是实际的类中。
答案 1 :(得分:0)
找到this SO question后终于得到了它-我不知道您也可以修补单个函数/类方法(unittest.mock.patch reference中未提及)。如此有效:
import unittest
from unittest.mock import patch
from util import api
def do_something():
api.logger.info("doing something")
api.send("some data")
@patch("util.api.send")
@patch("util.api.logger")
class Test(unittest.TestCase):
def test_do_something(self, mock_logger, mock_send):
do_something()
mock_send.assert_called()
mock_logger.info.assert_called()