Home > Enterprise >  Python - Mock requests with side_effect
Python - Mock requests with side_effect

Time:11-04

I'm trying to mock out my requests. get with a side effect. I would like to associate a different status_code for each side effect value but I didn't succeed so far.

    def test_func1(mocker):
        side_effect = ["Ok",'','','Failed']
    
        # This line should be changed
        fake_resp.status_code = 200
    
        fake_resp = mocker.Mock()
        fake_resp.json = mocker.Mock(side_effect=side_effect)
        
    
        mocker.patch("app.main.requests.get", return_value=fake_resp)
        
        # The func1 is executing multiple API calls using requests.get() and status_code is needed
        a = func1(a, b) 
    
        assert a == "something"

I have not been able to find a way (in the doc and SO) to associate the status_code for each mock request.

I was thinking about something like this but it's obviously not working:

    def test_func1(mocker):
        side_effect = [(status_code=200, return="Ok"),
                   (status_code=204, return=""), 
                   (status_code=204, return=""),
                   (status_code=500, return="Failed")]
        ....

Edit: add func1 code

from datetime import datetime, timedelta
import requests

def func1(days, delta_1):
    """
    days: number of days before first search (80, 25, 3)
    delta_1: number of days for the date range search (40, 20, 15)
    """
    now = datetime.now()
    start_date = now   timedelta(days=days)
    
    # Var to stop loop when price is found
    loop_stop = 0
    
    # Var to stop loop when search date is more than a year later
    delta_time = 0

    price = 0
    departureDate = "n/a"

    # For loop to check prices till one year. 
    while loop_stop == 0 and delta_time < (365 - days):
        date_range = (
            (start_date   timedelta(days=delta_time)).strftime("%Y%m%d")
              "-"
              (start_date   timedelta(days=delta_time   (delta_1 / 2))).strftime(
                "%Y%m%d"
            )
        )
        
        # Needs to be mocked
        response = requests.get("fake_url_using_date_range_var")
        
        if response.status_code == 204:
            print("No data found on this data range")
            delta_time  = delta_1
        elif response.status_code == 200:
            price = response.json()["XXX"][0]
            departureDate = response.json()["YYY"][0]
            loop_stop = 1
        else:
            raise NameError(
                response.status_code,
                "Error occured while querying API",
                response.json(),
            )
        
    return price, departureDate

CodePudding user response:

Possible solution with module unittest (not pytest)

I have written this answer before the adding of real function func1(), so I have created a file called my_file_01.py which contains my production function func1():

import requests

def func1():
    response1 = 'empty1'
    response2 = 'empty2'
    r = requests.get('http://www.someurl.com')
    if r.status_code == 200:
        response1 = r.json()
    r = requests.get('http://www.some_other_url.com')
    if r.status_code == 500:
        response2 = r.error_message
    return [response1, response2]

As you can see func1() calls requests.get() two times and checks the status code of responses.

I have inserted the test code in a different file with the following content:

import unittest
from unittest import mock
from my_file_01 import func1

def request_resp1(url):
    response_mock = mock.Mock()
    response_mock.status_code = 200
    response_mock.json.return_value = {'key1': 'value1'}
    return response_mock


def request_resp2(url):
    response_mock = mock.Mock()
    response_mock.status_code = 500
    response_mock.error_message = "Failed"
    return response_mock

class TestFunc1(unittest.TestCase):
    @mock.patch("my_file_01.requests")
    def test_func1(self, mock_requests):
        print("test func1()")
        mock_requests.get.side_effect = [request_resp1(""), request_resp2("")]
        [response1, response2] = func1()
        print("response1 = "   str(response1))
        print("response2 = "   str(response2))

if __name__ == "__main__":
    unittest.main()

The test file defines the test class TestFunc1 which contains the test method test_func1().

Furthermore in the file are defined 2 functions called request_resp1() and request_resp2(). These functions are used to define different response values so that the first call to requests.get() has success, while the second call to requests.get() failed.

If you try to execute the test code you can see that func1() return different values for the 2 different status_codes of the response.

CodePudding user response:

Based on the solution from @frankfalse, the two mocking functions can be replaced by a class.

class MockResponse:
    def __init__(self, json_data, status_code=requests.codes.ok):
        self.json_data = json_data
        self.status_code = status_code

    def json(self):
        return self.json_data
  • Related