Mock, Patch를 이용해 외부 API 의존성 테스트하기

외부 API에 의존하는 코드를 테스트할 때 발생하는 속도, 비용, 비결정성 문제를 Mock과 patch를 활용해 해결하는 방법

Mock과 patch의 필요성

외부 API 의존 코드 테스트 시 문제점:

  • 속도: 네트워크 호출로 인한 테스트 속도 저하

  • 비용: API 호출 횟수에 따른 비용 발생

  • 비결정성: 외부 서버 상태에 따른 테스트 결과 변동

  • 제약: 특정 상황(결제 실패, 인증 실패) 재현의 어려움

Mock과 patch 기본 개념

  • Mock: 실제 객체를 흉내 내는 가짜 객체로 반환 값과 예외를 설정 가능

  • patch: 특정 모듈/클래스/함수를 테스트 중 Mock 객체로 대체

patch 사용법

데코레이터 방식

@patch('weather.services.WeatherService.get_weather_for_city')
def test_get_weather_success(self, mock_get_weather):
    mock_get_weather.return_value = {"temperature": 25}
    response = self.client.get("/api/weather?city=Seoul")
    mock_get_weather.assert_called_once_with("Seoul")

컨텍스트 매니저 방식

def test_get_weather_success(self):
    with patch('weather.services.WeatherService.get_weather_for_city') as mock_get_weather:
        mock_get_weather.return_value = {"temperature": 25}
        response = self.client.get("/api/weather?city=Seoul")

핵심 원칙과 팁

patch 경로 설정 원칙

"정의된 곳이 아니라 사용되는 곳을 patch"

  • 틀린 방법: @patch('requests.get')

  • 올바른 방법: @patch('weather.services.requests.get')

autospec=True 활용

@patch('weather.services.WeatherService.get_weather_for_city', autospec=True)

원본 객체의 시그니처를 복제하여 더 안전한 Mock 생성

side_effect 활용

예외 발생과 동적 반환 값 제어:

# 예외 발생
mock_get_weather.side_effect = requests.RequestException("Connection failed")

# 호출마다 다른 값 반환
mock_api_call.side_effect = [{"id": 1}, {"id": 2}, ValueError("Limit reached")]

Mock 호출 검증

테스트 마지막에 Mock이 예상대로 호출되었는지 반드시 검증:

mock_get_weather.assert_called_once_with("Seoul")

Last updated