为什么在用unittest打补丁的时候会出现断言错误?

问题描述 投票:0回答:1

当我直接使用 unittest.mock.patch 来修补一个方法时,我可以使用 mock.assert_called_with() 函数来正确地断言该方法是否被某些值所调用。然而,当我直接给一个类打补丁时,我对该类本身或该类的任何方法使用的任何断言都会以 "AssertionError: 尽管类和方法都被调用了,但还是出现了 "AssertionError: Not Called"。

在unittest文档中,有一些成功地给类打补丁和使用断言的例子。unittest mock doc

这是我的测试,直接打上SiteMetaData类方法的补丁,打上EdgeAPIRoutes类的补丁。

边缘APIRoutes类打了补丁:

    @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类和方法的断言失败。根据文档,似乎这两种打补丁的方式都应该有自己的断言通过,但当我直接用unittest.mock.patch打一个方法时,我却可以使用EdgeAPIRoutes类和方法的断言。

python python-3.x python-unittest
1个回答
0
投票

正如@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
    """
    ...
© www.soinside.com 2019 - 2024. All rights reserved.