Python unittest.mock: 已调用修补的 class 方法但断言失败
Python unittest.mock: patched class method called but assertion fails
我正在尝试模拟实用程序的几个组件 class。虽然 assert_called()
对于一种方法没问题,但对于另一种方法却失败了,但我确信这两种方法都被调用了。我是 运行 Python 3.7.3 Windows 10.
我已经将我的场景精简到最基本的部分。
实用程序 class (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 中定义的文件。在生产环境中,它在提供实用程序 class 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()
代码。
这是一个棘手的问题。
问题是 api
class 在修补之前由测试 class 导入。来自 patch 的文档:
The target is imported when the decorated function is executed, not at decoration time.
由于您在测试 class 中导入 api
class 第一件事,所以当您 运行 测试时,那个家伙没有得到补丁。 Python 认为它已经导入。
请注意,logger
class 不是这种情况,因为您从未导入它。如果你有,第一次测试也会失败。
最简单的修复 - 将导入移动到测试函数中。
def do_something():
from util import api
api.logger.info("doing something")
api.send("some data")
现在 patch
将执行导入 - 因此模拟被放置在测试范围内而不是实际 class。
终于找到了this SO question - I wasn't aware that you can also patch individual functions/class methods (not mentioned in the 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()
我正在尝试模拟实用程序的几个组件 class。虽然 assert_called()
对于一种方法没问题,但对于另一种方法却失败了,但我确信这两种方法都被调用了。我是 运行 Python 3.7.3 Windows 10.
我已经将我的场景精简到最基本的部分。
实用程序 class (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 中定义的文件。在生产环境中,它在提供实用程序 class 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()
代码。
这是一个棘手的问题。
问题是 api
class 在修补之前由测试 class 导入。来自 patch 的文档:
The target is imported when the decorated function is executed, not at decoration time.
由于您在测试 class 中导入 api
class 第一件事,所以当您 运行 测试时,那个家伙没有得到补丁。 Python 认为它已经导入。
请注意,logger
class 不是这种情况,因为您从未导入它。如果你有,第一次测试也会失败。
最简单的修复 - 将导入移动到测试函数中。
def do_something():
from util import api
api.logger.info("doing something")
api.send("some data")
现在 patch
将执行导入 - 因此模拟被放置在测试范围内而不是实际 class。
终于找到了this SO question - I wasn't aware that you can also patch individual functions/class methods (not mentioned in the 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()