python - 为什么在使用 unittest 修补类时会出现断言错误?
问题描述
当我直接使用 unittest.mock.patch 修补方法时,我可以使用 mock.assert_call_with() 函数来正确断言该方法是否是使用某些值调用的。但是,当我直接修补类时,即使调用了类和方法,我在类本身或任何类方法上使用的任何断言都会返回“AssertionError:未调用”。
unittest 文档中有成功修补类和使用断言的示例:unittest mock doc
这是我使用直接修补 SiteMetaData 类方法和修补 EdgeAPIRoutes 类的测试:
测试
@patch("repo.routes.EdgeAPIRoutes")
@patch("repo.site_meta_data.SiteMetaData.get_repo_type")
@patch("repo.site_meta_data.SiteMetaData.get_edge_api_inputs")
def test_get_route_list_repo_interface_edge(self, mock_edge_api_inputs,
mock_get_repo_type, mock_edge_api_routes_class):
"""
Test that SiteMetaData.get_route_list is used
appropriately and returns expected values from
mock as a valid response object
"""
from usecase import interface
from usecase import request_objects
from usecase.response_objects import ResponseSuccess
from utils.utils import DatetimeString
all_filters = {"filters" : {"parent_company" : "parent_1",
"site" : "site_1",
"start_date" : DatetimeString("2020-01-01 00:00:00.000"),
"end_date" : DatetimeString("2020-05-01 00:00:00.000")
}
}
example_route_list = [
"route_1",
"route_2",
"route_3"
]
edge_inputs = {
"edge_site" : "edge_site_name",
"edge_key" : "ffff-ffff-ffff"
}
error_response = None
mock_edge_api_inputs.return_value = [
edge_inputs["edge_site"],
edge_inputs["edge_key"],
error_response
]
mock_get_repo_type.return_value = [
"Minestar Edge",
error_response
]
mock_edge_api_routes_class.get_route_list.return_value = [
example_route_list,
error_response
]
resp = interface.get_route_list(request=request_objects.RouteList.request_wfilters(all_filters))
mock_get_repo_type.assert_called_with(all_filters["filters"]["parent_company"],
all_filters["filters"]["site"])
mock_edge_api_inputs.assert_called_with(all_filters["filters"]["parent_company"],
all_filters["filters"]["site"])
mock_edge_api_routes_class.get_route_list.assert_called_with(all_filters["filters"]["start_date"],
all_filters["filters"]["end_date"])
mock_edge_api_routes_class.assert_called_once_with(edge_inputs["edge_site"], edge_inputs["edge_key"])
self.assertTrue(bool(resp))
self.assertEqual(resp.type_, ResponseSuccess.SUCCESS)
self.assertEqual(resp.value["routes"], example_route_list)
方法
def get_route_list(request):
"""
"""
interfacelog.info("running get_route_list")
if bool(request):
repo_type, repo_type_error = site_meta_data.get_repo_type(request.filters["parent_company"],
request.filters["site"])
if repo_type_error is not None:
'''
handle any get repo type system errors
'''
pass
if repo_type == "Minestar Edge":
edge_site, edge_key, edge_error = site_meta_data.get_edge_api_inputs(request.filters["parent_company"],
request.filters["site"])
ear = EdgeAPIRoutes(edge_site, edge_key)
rl, routes_error = ear.get_route_list(request.filters["start_date"],
request.filters["end_date"])
if routes_error is not None:
'''
handle any get routes system error
'''
pass
success_resp = response_objects.ResponseSuccess()
success_resp.value = {"routes" : rl}
return success_resp
else:
'''
handle and errors due to an invalid request
'''
return response_objects.ResponseFailure.build_from_invalid_request_object(request)
结果
SiteMetaData 修补方法的断言通过,而 EdgeAPIRoutes 类和方法的断言失败。根据文档,似乎两种修补方法都应该通过它们的断言。
解决方案
正如@MrBeanBremen 在他的评论中指出的那样,修补类的位置是关键。在我的问题中,我在源代码处对它们进行了修补,但是由于它们已导入到我的 interface.py 文件中,因此需要在该位置进行修补。
此外,由于 SiteMetaData 是在全局级别实例化的,因此需要修补其实例而不是类本身。由于 EdgeAPIRoutes 是在 get_route_list 函数中实例化的,因此需要对类本身进行修补。然后要将返回值分配给 EdgeAPIRoutes 的实例方法,需要使用修补类的 return_value:
mock_edge_api_routes_class.return_value.get_route_list.return_value = [
example_route_list,
error_response
]
由于 site_meta_data 实例是直接打补丁的,所以实例方法值可以直接在补丁中赋值:
mock_site_meta_data.get_edge_api_inputs.return_value = [
edge_inputs["edge_site"],
edge_inputs["edge_key"],
error_response
]
接口.py
import logging
from repo.site_meta_data import SiteMetaData
from repo.routes import EdgeAPIRoutes
from usecase import response_objects
interfacelog = logging.getLogger(__name__)
site_meta_data = SiteMetaData()
def get_route_list(request):
"""
"""
interfacelog.info("running get_route_list")
if bool(request):
repo_type, repo_type_error = site_meta_data.get_repo_type(request.filters["parent_company"],
request.filters["site"])
if repo_type_error is not None:
'''
handle any get repo type system errors
'''
pass
if repo_type == "Minestar Edge":
edge_site, edge_key, edge_error = site_meta_data.get_edge_api_inputs(request.filters["parent_company"],
request.filters["site"])
ear = EdgeAPIRoutes(edge_site, edge_key)
rl, routes_error = ear.get_route_list(request.filters["start_date"],
request.filters["end_date"])
if routes_error is not None:
'''
handle any get routes system error
'''
pass
success_resp = response_objects.ResponseSuccess()
success_resp.value = {"routes" : rl}
return success_resp
else:
'''
handle and errors due to an invalid request
'''
return response_objects.ResponseFailure.build_from_invalid_request_object(request)
test_interface 函数补丁设置
@patch("usecase.interface.EdgeAPIRoutes")
@patch("usecase.interface.site_meta_data")
def test_get_route_list_repo_interface_edge(self, mock_site_meta_data,
mock_edge_api_routes_class):
"""
Test that SiteMetaData.get_route_list is used
appropriately and returns expected values from
mock as a valid response object
"""
...
推荐阅读
- python - 导入到另一个 .py 文件
- jenkins - 在 Jenkins Job 中选择版本后动态填充文件夹的路径
- python-3.x - Google Cloud Monitoring Api - 400 无法写入一个或多个 TimeSeries 遇到重复的 TimeSeries
- javascript - npm copyfiles 不工作,给出 ELIFECYCLE 错误
- kotlin - Kotlin 标准函数创建 if null 并返回 all in one
- android - 如何在 Android 中显示自己的地图?
- javascript - 实例化一个日期对象,给定时间作为字符串和时区?
- linux - “NoneType”对象没有属性“secret_key”
- java - 如何在 apache camel rest api 中进行自定义错误处理?
- c - 在 Windows msvc 上编译 .c/.h 文件时,如何通过 C/C++ 智能感知(包括/索引)从 VS 代码中排除 cpp 相关文件