Mocking / Mockito

2018-05-22

테스트 주도 개발에 대해서 검색을 하다보면 Mockito, Hamcrest와 같은 키워드를 만날 수 있습니다. 그 중에서 Mockito 프레임워크에 대해서 알아보았습니다.

Mocking

Mocking은 실제 객체의 동작을 흉내내는 가짜 객체를 만드는 것을 의미합니다. 가짜 객체를 만드는 이유는 유닛 테스트를 할 때, 연관되어 있는 다른 리소스와는 무관하게 어떠한 유닛을 테스트하기 위해서입니다.

한 가지 예를 들어 맥도날드 햄버거를 주문할 수 있는 앱이 있다고 해볼까요? 앱에는 햄버거 목록 가져오기, 주문 내용 전달 등 수 많은 기능들이 있습니다. 이렇게 프로그램을 구성하는 각각의 기능을 테스트하는 것을 Unit Test라고 부릅니다.

문제는 햄버거 목록을 가져오기 위해서는 실제 햄버거 저장소와 서버가 있어야 한다는 것이죠. 이러한 외부의 리소스를 Dependency, 우리 말로 의존성이라고 이야기합니다.

서버는 앱과 다른 생명 주기로 움직이기 때문에, 같은 요청에 대해서 상황에 따라 다른 결과를 반환할 수 있습니다. 거기다가 네트워크를 사용하기 때문에 느리기까지 합니다.

Mocking을 사용해서 외부 리소스를 가짜로 흉내내면 이러한 의존성을 서버가 아닌 개발자가 컨트롤 할 수 있습니다. 햄버거 리스트를 가져와서 보여주는 기능을 테스트할 때는 항상 동일한 햄버거 종류를 반환하도록 하는 등의 작업이 가능합니다.

아래는 간단하게 작성해본 햄버거 API와 Mock입니다.


Mockito

이러한 Mocking을 손으로 직접 할 수도 있지만, Mockito를 이용하면 더욱 편하게 할 수 있습니다.

Mockito의 기능은 크게 가짜 객체와 그 동작을 정의하는 Stubbing과, 동작의 실행 여부를 체크하는 Verification로 볼 수 있습니다. 실제 코드를 보면서 다시 이야기해볼게요.

이번에는 맥도날드 햄버거 리스트를 가져온 다음 첫 번째 메뉴를 강조하는 기능을 만들어볼까요? Junit4와 Mockito를 사용해서 테스트를 만들어보았습니다. (View는 햄버거를 보여주는 역할입니다.)


@Mock
private HamburgerView mockView;
@Mock
private HamburgerApi mockApi;

MockitoAnnotations.initMocks(this);

먼저 @Mock 어노테이션을 통해서 어떤 필드를 Mocking할 것인지 선언하고, MockitoAnnotations.initMocks(this)를 실행해서 초기화합니다.

when(mockApi.hamburgerList()).thenReturn(hamburgers);

when ~ then 의 구조로 mockApi.hamburgerList()를 실행했을 때 hamburgers를 반환해라 라는 문장입니다. 메서드의 동작을 Stubbing하는 부분이죠.

presenter.loadHamburgers();

// Verify invocation of methods.
verify(mockApi).hamburgerList();
verify(mockView).emphasize(hamburgers.get(0));

presenter.loadHamburgers()는 내부적으로 mockApi.hamburgerList()를 호출하고, 받아온 햄버거 리스트를 mockView에 뿌려주어야 합니다. 이 동작이 제대로 실행되는지 체크하기 위해서 verify()메서드를 사용하고 있습니다.

보시는 것처럼 Mockito 프레임워크는 Build-Operate-Check의 패턴을 가지고 있습니다. when()을 통해서 필요한 것들을 준비하고, presenter의 실제 동작을 수행한 다음 verify()로 동작을 검증합니다.

이렇게 동작을 구분하면 가독성이 좋아지는 효과가 있습니다. Clean Code에서 말하는 깨끗한 테스트 코드를 만드는 데에도 도움이 되지요.


Mocking을 할 때 주의해야 할 것들을 몇 가지 알려드릴게요.

  • 테스트할 대상은 Mocking하지 마세요. 테스트의 대상이 되는 객체(앞의 예시로 들면 Presenter)는 Mocking을 하면 안됩니다. Mocking의 목적은 테스팅이 용이하도록 의존성을 분리하는 것입니다.

  • 과도한 Mocking은 좋지 않습니다. Mocking하는 클래스가 열 개 가까이 된다거나, 많은 기능을 Mocking하고 있다면 코드가 테스트하기 어려운 구조로 작성되었다는 신호입니다. 추상화, 인터페이스 등을 통해서 코드를 정리하세요.

  • Mocking은 어디까지나 Mocking일 뿐, 실제 코드와는 동작이 다를 수 있습니다. 신뢰할 수 있는 Mock을 작성하고, 유닛 테스트 말고도 Integration Test나 실제 테스트를 해보세요.


Summary

정리하자면,

Mocking은 실제 객체를 흉내내는 것을 뜻하는데, 그 목적은 유닛 테스트를 할 때 외부 리소스에 대한 의존성을 제거하기 위해서입니다.

Mockito는 이러한 Mocking과 동작의 검증을 도와주는 유닛 테스팅 프레임워크입니다.

정말 다양한 기능들이 있는데, 필요함을 느낄 때 직접 찾아가면서 써보시는 걸 추천드려요! 아래는 관련 링크들입니다.

Mockito 사이트

Mockito 가이드