pythonmockingpytestpython-mock

Changing the second result of a function call with mock


I have a loop that looks like this:

for i in range(len(some_list)):
    response = requests.post(some_url, some_params)
    if response.status_code != HTTPOk:
       # do something

What I would like to do is change response of requests.post in the second iteration of the loop. From within my test, I know I can do something like:

mock_response = mock.Mock()
mock_response.status_code = 404
with mock.patch(mymodule.requests.post, return_value=mock_response):
   mymodule.some_function()

But that only seems to work for one status_code. I looked at side_effect, and it looks like I can iterate through the loop like so:

mock_response.side_effect = [
    mock.Mock(status_code=400), mock.Mock(status_code=200)
]
with mock.patch(mymodule.requests.post, return_value=mock_response):
   mymodule.some_function()

However, it looks like it doesn't actually get the "right" status code. What's the best way of changing the behavior in side_effect or return_value to properly get the behavior I want? I think side_effect is what I want, but I'm not sure what the best way is to mock the responses.


Solution

  • The simpler and cleaner way to do it is

    with mock.patch("mymodule.requests.post", 
                    side_effect=[Mock(status_code=400), Mock(status_code=200)]) as mock_post:
       mymodule.some_function()
    

    patch create mock_post object by MagicMock(side_effect=mock_responses) and replace mymodule.requests.post reference. You can also use mock_post to check post() calls by something like:

    mock_post.assert_has_calls([mock.call(first_url, first_params), mock.call(second_url, second_params)])
    

    You can do the same work by build and configure mock_post before and then pass it as new parameter (the second patch argument) but by this way you have 2 downside

    1. More code to write
    2. Lose the ability to use autospec=True options in patch

    Autospeccing is a very powerful feature of mock framework that prevent a lot of silly bug both in test and code.