Mock
은 수많은 요소로 구성된 소프트웨어에서 요소끼리 어떻게 상호작용하며 동작하는지 테스트하기 위해 각 구성요소처럼 동작하는 "가짜" 구성요소를 의미합니다. Mock
객체는 실제 구성요소와 같은 인터페이스를 제공하지만 실제로 실제 요소와 같이 동작하지는 않으며, 미리 정의된 응답이나 값을 반환하는 등, 개발자가 정의한대로 동작하게 됩니다.
Mock
객체를 사용하게 되면 의존하는 다른 컴포넌트의 결과에 의존하지 않고 테스트를 진행하는 구성요소 내부의 로직에 의해 테스트 결과가 정해지기 때문에 소프트웨어 구성요소의 독립적인 테스트가 가능해집니다. 이와 같은 장점으로 인해 Mock
객체는 단위 테스트에서 매우 유용하게 사용되고 있습니다.
Mock
을 사용하면 다양한 테스트를 구현하는데 편의성이 있습니다. Mock
객체는 함수 호출 결과를 개발자가 직접 정의할 수 있기 때문에, 다양한 시나리오를 구성하는데 용이합니다.
Mock
을 사용하게 되면 테스트를 진행하기 위해 시스템을 구성하는 수많은 요소들을 직접 setup할 필요가 없어지기 때문에 테스트 속도가 빨라질 수 있습니다.
Mock
객체는 수정이 용이하기 때문에 개발자가 실제 배포 환경에서 발생할 수 있는 다양한 시나리오나 엣지 케잇스들을 구현하기 간편합니다.
Mock
은 어떤 메소드가 어떻게 호출되었으며, 어떠한 값을 반환하였는지에 대한 정보를 제공합니다. 이러한 정보들을 바탕으로 코드를 디버깅할 수 있습니다.
Mock
객체는 개발자들끼리 쉽게 공유할 수 있습니다. 따라서 개발자들은 간편하게 테스트 작업에 협업할 수 있으며, 다양한 실행 환경에서도 일관적인 테스트 결과를 얻을 수 있습니다.
이처럼 다양한 장점을 지닌 Mock
객체는 단위테스트 뿐만 아니라 통합테스트에서도 사용될 수 있습니다. 필자의 경우 Oauth API, PG API 등 외부 API에 의존하는 API에서 외부 API들을 Mocking
함으로써 서비스 내부 로직에 관한 테스트를 진행하였습니다. 다음으로는 pytest 와 unittest 라이브러리로 mocking 을 구현하는 방법에 대해 정리해보겠습니다.
pytest에서는 monkeypatch라는 fixture를 제공합니다. monkeypatch는 런타임 환경에서 코드를 동적으로 변경하는 것을 의미합니다. monkeypatch fixture를 사용하면 테스트시 모듈, 클래스, 객체를 임시로 변경하거나 대체하여 사용할 수 있어 코드의 일부 요소를 mocking할때 유용하게 사용할 수 있습니다.
monkeypatch.setattr(target, name, value, raising=True)
monkeypatch.setattr
메소드는 모듈, 클래스, 객체의 속성을 임시로 변경할 때 사용됩니다. 주로 단위테스트에서 클래스나 메소드의 동작을 테스트하기 위해 특정 요소를 mocking 하고자 할 때 사용합니다.
target
mock으로 대체할 대상 클래스, 객체, 모듈을 입력합니다. 대체할 대상의 경로를 문자열로 입력하면 해당 리소스가 사용될때 함수에 정의된 다른 mock 으로 대체되거나 미리 정의해둔 값을 반환하도록 합니다.
name
변경할 속성, 메소드의 이름을 문자열로 입력합니다.
value
name 대신 대체할 새로운 함수, 메소드, 객체, 클래스를 명시합니다.
raising
대체할 요소가 존재하지 않을 경우 예외를 발생시킬지의 여부를 Boolean 값으로 설정합니다.
monkeypatch.setattr
메소드로 원하는 객체를 mocking 하기 위해서는 대체하고자하는 객체의 이름을 명확히 명시할 수 있어야합니다.
저는 API를 테스트하면서 외부 API를 호출하는 클래스를 Mocking 하여 테스트를 진행하였습니다. kakao Oauth 서비스를 사용하여 인증/인가를 구현하였는데, 카카오 로그인 url을 요청하는 클래스를 mocking하였습니다.
# tests/mock.py
class MockKakaoOauthService:
def request_login_url(self, **kwargs):
return ## 테스트시 반환할 값
우선 기존 API 에서 사용중인 KakaoOauthService 클래스를 대체할 Mock 클래스를 구현하였습니다. tests 패키지에 mock 모듈을 생성하여 내부에 클래스를 정의하였고, 테스트시 내부 로직에서 사용되는 메소드를 정의하였고, 알맞는 값을 반환하도록 구현하였습니다.
# tests/test_auth.py
import mock
@pytest.mark.django_db
def test_given_nothin_when_request_kakao_oauth_login_url_then_return_200(
client, monkeypatch):
# given
monkeypatch.setattr('services.kakao_service.KakaoOauthService', mock.MockKakaoOauthService())
# when
response = client.get(
reverse('kakao-login-url'),
content_type='application/json'
)
response_data = json.loads(response.content).get('data')
# then
assert response.status_code == status.HTTP_200_Ok
# 다른 assertion 문
다음은 API 테스트 코드에서 monkeypatch fixture를 사용하여 Mock 객체로 테스트가 진행되도록 합니다.