Reputation: 12047
Using pytest
, I wish to mock a function that can raise several exceptions. My app will catch the exception and create a response object, and I need to assert that each response contains the correct message, type, and in reality several other properties.
In the first instance, I have created a separate fixture to mock the function and raise each exception, and then I'm passing those fixtures in to a test with a series of events. However, because my fixtures are mocking the same function, the exception raised for every test is the same - in this case, that would be mock_exc_2
, the last fixture passed to the test.
One thing I know will work is to separate the test function into multiple functions, but that seems inefficient because any future change would need to be made to multiple functions.
What is the most appropriate / efficient way to do this with with pytest
?
@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.
In this scenario, only the last test is successful because both mock_exc_1
and mock_exc_2
are mocking the same function.
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']
Here the tests will pass, because each test is only using the fixture that raises the correct exception for the mocked function.
However, in reality there are more than two exceptions to test, and having to maintain a parameter with the parametrize
args and a function to test each exception seems inefficient and prone to error when a change is made.
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']
Upvotes: 0
Views: 1206
Reputation: 12047
It seems that at the moment there is no "proper" way to do this. However, it is possible to do this by using the 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']
Upvotes: 1