使用 'pytest' 模拟一个可以 return 多个异常的函数

Use 'pytest' to mock a function that can return multiple exceptions

使用 pytest,我希望模拟一个可以引发多个异常的函数。我的应用程序将捕获异常并创建一个响应对象,我需要断言每个响应都包含正确的消息、类型,实际上还有其他几个属性。

首先,我创建了一个单独的固定装置来模拟函数并引发每个异常,然后我将这些固定装置传递给具有一系列事件的测试。但是,因为我的装置正在模拟相同的函数,所以每次测试引发的异常都是相同的——在这种情况下,那将是 mock_exc_2,最后一个传递给测试的装置。

我知道的一件事是将测试函数分成多个函数,但这似乎效率低下,因为未来的任何更改都需要对多个函数进行。

使用 pytest 执行此操作最合适/最有效的方法是什么?

'conftest.py'

中的赛程
@pytest.fixture(scope='function')
def mock_exc_1(mocker):
    def mock_response(self, path):
        raise MissingOrgIdException()
    
    mocker.patch('proxy.app.mcpcore.ProxyRequest._validate_org_id', mock_response)

@pytest.fixture(scope='function')
def mock_exc_2(mocker):
    def mock_response(self, path):
        # Parameter values are not emitted in the error message that is included in the response to the user.
        raise InvalidOrgIdException('xxx', 'xxx')
    
    mocker.patch('proxy.app.mcpcore.ProxyRequest._validate_org_id', mock_response)

# Working fixtures for 'event and 'mock_context' go here.

'test_events.py'

中的测试失败

在这种情况下,只有最后一个测试成功,因为 mock_exc_1mock_exc_2 都在模拟同一个函数。

bad_request_args = ('event, expected',
    [
        (
            '400-org-id-missing.json',
            {
                'message': 'URI path does not include an organisation ID.',
                'type': 'MissingOrgIdException'
            }
        ),
        (
            '400-org-id-invalid.json',
            {
                'message': 'Invalid organisation ID in URI path.',
                'type': 'InvalidOrgIdException'
            }
        )
    ]
)

@pytest.mark.parametrize(*bad_request_args, indirect=['event'])
def test_400_events(event, expected, mock_context, mock_exc_1, mock_exc_2):
    response = lambda_handler(json.loads(event), mock_context)
    body = json.loads(response['body'])
    assert body['errorMessage'] == expected['message']
    assert body['errorType'] == expected['type']

'test_events.py'

中的工作测试

此处测试将通过,因为每个测试仅使用为模拟函数引发正确异常的装置。

然而,实际上有两个以上的异常需要测试,并且必须使用 parametrize args 维护一个参数和一个函数来测试每个异常似乎效率低下并且在进行更改时容易出错.

bad_request_args_1 = ('event, expected',
    [
        (
            '400-org-id-missing.json',
            {
                'message': 'URI path does not include an organisation ID.',
                'type': 'MissingOrgIdException'
            }
        )
    ]
)

bad_request_args_2 = ('event, expected',
    [
        (
            '400-org-id-invalid.json',
            {
                'message': 'Invalid organisation ID in URI path.',
                'type': 'InvalidOrgIdException'
            }
        )
    ]
)

@pytest.mark.parametrize(*bad_request_args_1, indirect=['event'])
def test_400_events_1(event, expected, mock_context, mock_exc_1):
    response = lambda_handler(json.loads(event), mock_context)
    body = json.loads(response['body'])
    assert body['errorMessage'] == expected['message']
    assert body['errorType'] == expected['type']

@pytest.mark.parametrize(*bad_request_args_2, indirect=['event'])
def test_400_events_2(event, expected, mock_context, mock_exc_2):
    response = lambda_handler(json.loads(event), mock_context)
    body = json.loads(response['body'])
    assert body['errorMessage'] == expected['message']
    assert body['errorType'] == expected['type']

目前似乎没有“正确”的方法来做到这一点。但是,可以使用 request.getfixturevalue('fixture')

bad_request_args = ('event, expected, mock_fixture_name',
    [
        (
            '400-org-id-missing.json',
            {
                'message': 'URI path does not include an organisation ID.',
                'type': 'MissingOrgIdException'
            },
            'mock_exc_1'
        ),
        (
            '400-org-id-invalid.json',
            {
                'message': 'Invalid organisation ID in URI path.',
                'type': 'InvalidOrgIdException'
            },
            'mock_exc_2'
        )
    ]
)

@pytest.mark.parametrize(*bad_request_args, indirect=['event'])
def test_400_events(event, expected, mock_fixture_name, mock_context, request):
    request.getfixturevalue(mock_fixture_name)
    response = lambda_handler(json.loads(event), mock_context)
    body = json.loads(response['body'])
    assert body['errorMessage'] == expected['message']
    assert body['errorType'] == expected['type']