Django

E2E 테스트할 때 외부 API Mock으로 대체하기

r잡초처럼 2023. 3. 16. 01:29

외부 API가 있는 view를 E2E 테스트를 하고 싶다. 하지만 테스트를 할 때마다 외부 API를 요청하는 건 자원 낭비다.(사실 다른 이유가 있을 순 있는데 내가 생각하기에는) 예를 들어 파일 업로드를 S3에 한다고 치자. 만약 테스트 할 때마다 S3에 업로드한다면 말이 되는가?(사실 내가 했다. 근데 뭔가 이상한 점을 눈치챘으니 봐주자)

이럴 때 Mock을 써보자!

 

테스트코드

예를 들어보자. 당신은 foo api를 테스트해보고 싶다. foo api는 외부 API를 요청받아 보여준다. 그런데 외부 API는 트래픽 제한이 있다. 100번의 트래픽 제한 중에 50번을 테스트한다고 치면 실제 서비스에서는 50번 밖에 사용자에게 못 보여준다. 어쩔 건가?

아무런 조치를 취하지 않은 API Test

test.py:

class FooAPITests(APITestCase):
    def test_foo_api_view(self, mock_get, mock_get2):
        url = "/foo/"
        response = self.client.get(url, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)

service.py

def request_external_api():
    ...
    return {...}

views.py

from app.service import request_external_api


def foo_list(request):
    ...
    response = request_external_api()  # 최대 100번만 요청할 수 있는 외부 API
    return render(request, ..., {
        'foo_list': response,
    })

이제 Mock을 이용하여 테스트를 진행해 보자. 간단하게 return value만 조작해 보자.

class FooAPITests(APITestCase):
    @patch("app.service.request_external_api")
    def test_foo_api_view(self, mock_get):
        url = "/foo/"
        mock_get.return_value = {"test": "mock is success"}
        response = self.client.get(url, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)

이렇게 하고 console에 출력하도록 코드를 수정해 보자.

from app.service import request_external_api


def foo_list(request):
    ...
    response = request_external_api()  # 최대 100번만 요청할 수 있는 외부 API
    print(response)
    return render(request, ..., {
        'foo_list': response,
    })

이렇게 하고 테스트를 해보면 해당 True로 찍힌 모습을 볼 수 있다.

console에 mock이 찍힌 모습

혹시나 외부 API가 2개라면 다음과 같이 하면 되더라.

class FooAPITests(APITestCase):
    @patch("app.service.request_external_api2")
    @patch("app.service.request_external_api")
    def test_foo_api_view(self, mock_get_api, mock_get_api2):
        url = "/foo/"
        mock_get.return_value = {"test": "mock is success"}
        mock_get_api2.return_value = {"test2": "mock2 is success"}
        response = self.client.get(url, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)