[Answered ]-Django unit test – patch multiple requests for external api

1πŸ‘

βœ…

I simplified your source code for ease of testing so that we can concentrate on the problem which are the external requests.

./utils.py

import requests

def login_decorator(func):
    def wrapper(self, request, *args, **kwargs):
        # first requests, which gives me an info about user.
        response  = requests.get(
            'https://kapi.kakao.com/v2/user/me', 
            headers={'Authorization': 'Bearer access_token'}
        )

        request.user = response.json()['kakao_account']['email']
 
        return func(self, request, *args, **kwargs)

    return wrapper

./views.py

import json
import requests

from utils import login_decorator


class BiddingView:
    #it uses login_decorator above
    @login_decorator
    def post(self, request, art_id):
        print(f"The current user is {request.user}")

        #second requests, with post method
        response = requests.post(
            'https://kapi.kakao.com/v2/api/talk/memo/default/send',
            headers = {'Authorization'   : 'Bearer token'},
            data    = {"template_object" : json.dumps({'message':'contents'})}
        )

        return response.text

Solution 1 – Manual patching of requests for each source file

You can stack the unittest.mock.patch decorator, one after the other.

from unittest.mock import MagicMock, patch

from views import BiddingView


class MockLoginResponse:
    def json(self):
        return {'kakao_account' : {'email' : 'test@test.com'}}


class MockViewResponse:
    text = "He alone, who owns the youth, gains the future."


@patch('utils.requests.get', MagicMock(return_value=MockLoginResponse()))
@patch('views.requests.post', MagicMock(return_value=MockViewResponse()))
def test_kakao_message_success():
    response = BiddingView().post(
        request=MagicMock(),
        art_id="some art"
    )
    print(f"Response: {response}")

Output:

__________________________________________________________________________________ test_kakao_message_success ___________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------
The current user is test@test.com
Response: He alone, who owns the youth, gains the future.

Solution 2.1 – Instead of patching requests per file, patch the exact target request

This requires you to install library https://pypi.org/project/requests-mock/

from unittest.mock import MagicMock

import requests_mock

from views import BiddingView


def test_kakao_message_success_with_library():
    with requests_mock.Mocker() as requests_mocker:
        # Mock the external requests
        requests_mocker.get(
            "https://kapi.kakao.com/v2/user/me",
            json={'kakao_account' : {'email' : 'test@test.com'}},
        )
        requests_mocker.post(
            "https://kapi.kakao.com/v2/api/talk/memo/default/send",
            text="He alone, who owns the youth, gains the future.",
        )

        response = BiddingView().post(
            request=MagicMock(),
            art_id="some art"
        )
        print(f"Response: {response}")

Output:

  • Same as above

Solution 2.2 – Instead of patching requests per file, patch the exact target request. But now, apply it automatically to any test using pytest’s autouse feature.

from unittest.mock import MagicMock

import pytest
import requests_mock

from views import BiddingView


@pytest.fixture(autouse=True)
def setup_external_requests():
    with requests_mock.Mocker() as requests_mocker:
        # Mock the external requests
        requests_mocker.get(
            "https://kapi.kakao.com/v2/user/me",
            json={'kakao_account' : {'email' : 'test@test.com'}},
        )
        requests_mocker.post(
            "https://kapi.kakao.com/v2/api/talk/memo/default/send",
            text="He alone, who owns the youth, gains the future.",
        )

        # It is required to perform a yield instead of return to not teardown the requests_mocker
        yield requests_mocker


def test_kakao_message_success_with_library_2():
    response = BiddingView().post(
        request=MagicMock(),
        art_id="some art"
    )
    print(f"Response: {response}")

Output:

  • Same as above

Leave a comment