Home > other >  pytest mocker two separate external calls for the one process
pytest mocker two separate external calls for the one process

Time:07-14

I have a process that executes a method, let's say called 'save_details', which runs a loop to go out to an external source twice. I want to be able to mock the two responses that would be returned to 'save_details', to give me two different IDs. I can do it when I have one response, but it doesn't seem to work when I need two mock responses to be returned.

So I have the following example fixtures.

Mock 1

@pytest.fixture
def test_201_mock1(mocker) -> None:
    """
    Test return data for the 201 response.
    """
    mocker.patch(
        "save_details",
        return_value=[
            "201",
            {
                "ACK": {
                    "id": "e944126b-db78-4711-9f97-83c2cd1e09a4",
                }
            },
        ],
    )

Mock 2

@pytest.fixture
def test_201_mock2(mocker) -> None:
    """
    Test return data for the 201 response.
    """
    mocker.patch(
        "save_details",
        return_value=[
            "201",
            {
                "ACK": {
                    "id": "4758a428-8f33-4dc8-bb64-a500855f9a8c",
                }
            },
        ],
    )

And I have my test:

async def test_create_valid_resource_201(
    test_201_mock1,
    test_201_mock2,
    ...
) -> None:
    """
    Tests if a valid POST request returns a 201 response.
    """
    #rest of test
    ...

The 'test_create_valid_resource_201' test will simply run the 'save_details' method I have. What happens is I basically get the ID from test_201_mock2 duplicated.

4758a428-8f33-4dc8-bb64-a500855f9a8c
4758a428-8f33-4dc8-bb64-a500855f9a8c

How would I get pytest to recognise that there are two mocks to be returned, one for each iteration of my loop in 'save_details'? Do I create one mock with the two responses for example?

CodePudding user response:

Side effect is meant just for this purpose - it can be used to return different values in the same mock. This example is taken from the documentation - 3 consecutive calls to the mock would produce different results as specified in side_effect:

>>> mock = MagicMock()
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)

In your example you can have a single fixture to set the side_effect like so:

@pytest.fixture
def test_201_mock(mocker) -> None:
    """
    Test return data for the 201 response.
    """
    mocker.patch(
        "save_details",
        side_effect=[
            ["201",
            {
                "ACK": {
                    "id": "e944126b-db78-4711-9f97-83c2cd1e09a4",
                }
            }],
            ["201",
            {
                "ACK": {
                    "id": "4758a428-8f33-4dc8-bb64-a500855f9a8c",
                }
            }]
        ],
    )
  • Related