Java와 Junit을 활용한 실용주의 단위 테스트 - Mock테스트 2편
2021, Feb 21
mock도구를 사용하여 테스트를 단순화하기
stub대신 MOCKITO 사용하기
- 이전 포스트에서 http, 데이터등 외부서비스로 부터 데이터 응답을 얻어 내는 일을 stub으로 대체 하여 테스트 할 수 있다는 개념을 정리 하였다.
- 또한 우리의 코드들을 제대로 검증 하려면 그 외부 서비스에 전달 되는 파라미터에 대한 검증이나 응답값에 제대로 전달 되지 않는 예외 사항에 대한 검증도 필요하기 때문에 stub코드에 기능이 추가로 필요하다는 것도 알 수 있었다.
- 하지만, 여러 예외 적인 케이스의 테스트를 구현하려고 한다면 많은 스텁 구현객체가 필요하게 될 것이다.
-
이런 반복적인 요소들을 해결해 주는 도구가 spring에 있으니 바로
Mockito가 되겠다. 예제 코드를 통해서 만나보자.import static org.mockito.Mockito.*; Http http = mock(Http.class); when(http.get(contains("lat=38.000000&lon=-104.000000"))) .thenReturn( "{\"address\":{" + "\"house_number\":\"324\"," // ... + "\"road\":\"North Tejon Street\"," + "\"city\":\"Colorado Springs\"," + "\"state\":\"Colorado\"," + "\"postcode\":\"80903\"," + "\"country_code\":\"us\"}" + "}"); ...
Http http = mock(Http.class);이부분은 Http mock 객체를 생성하는 부분이다.when()메소드는 mock객체에 전달 파라미터에 대한 검증 조건을 명시한다. 이전 stub 예제에서 http 전달 파라미터가 요건에 맞지 않았을 경우 오류가 발생 되도록 처리한 부분과 동일하다.thenReturn()는 메소드를 호출 하여 전달 파리미터에 요구하는 조건이 충족 되었을때 응답값을 설정한다.- 이렇게
Mockito를 이용하면 다양한 설정에 대한 stub객체를 여러개 만들지 않고 mock테스트가 가능하다. - 또한 응답 객체에 대한 처리 코드를 인증하고자 할때 verfy() 함수를 사용하면 된다. (다음 포스트에서 정리예정)
mock객체 주입하기
- 위 예제 코드와 같이 테스트 메소드 마다 매번 mock객체를 만들필요 없이, Test클래스에 주입이 가능하다.
public class AddressRetrieverTest { @Mock private Http http; // (1) @InjectMocks private AddressRetriever retriever; // (2) @Before public void createRetriever() { retriever = new AddressRetriever(); MockitoAnnotations.initMocks(this); (3) } ... }
@Mock private Http http;@Mock 애너테이션을 사용하여 mock 인스턴스를 생성한다.@InjectMocks private AddressRetriever retriever;http를 사용하는, 즉 mock 객체를 주입받을 대상(테스트 대상인 클래스) 클래스의 인스턴스 변수를 선언한다.MockitoAnnotations.initMocks(this);this인수는 테스트 클래스 자체를 가르킨다. mockito는 테스트 클래스에서 @Mock 애너테이션이 붙은(예제에서는 http) 필드를 가져와서 각각에 대해 목 인스턴스를 생성한다. 그 다음 @InjectMocks 애너테이션이 붙은 필드에 주입시켜준다.
Mock을 사용할 때 중요한 것
- Mock을 통해 테스트 하는 것은 실제 동작을 대신한다는 것을 잊지 말아야 한다. 작성한 목 테스트가 제대로 기능을 하는지에 대해서는 아래와 같이 자신에게 몇가지 질문을 할 필요가 있다고 책에서는 말한다.
- mock이 production 코드(실제 서비스 되는 라이브코드)의 동작을 올바르게 묘사하고 있는가?
- 프로덕션 코드는 생각하지 못한 다른 형식으로 반환하는가?
- 프로덕션 코드는 예외를 던지는가?
- null을 반환하는가?
- Mock테스트는 프로덕션 코드를 직접 테스트하고 있지는 않다. Mock을 도입하면 테스트 커버리지에서 gap을 형성할 수 있음을 인지해야 한다. 실제 클래스의 종단 간 사용성을 보여 주는 적절한 상위 테스트(종합 테스트)가 있는지 확인 해야 한다.
개인 생각 정리
- mockito를 통해서 반복되는 mock테스트를 위한 코드를 줄일 수 있다고 하지만, 실제 API나, DB등과의 연결고리를 테스트 할 수 없다는 점에서 보다 촘촘한 테스트를 위해서는 여러 예외사항에 대한 테스트 상황을 만들어야 한다는 점에서, 노가다에서 벗어날 수는 없는 것 같다.
- 결국 코드를 검정하는 테스트코드는 개발자의 노력과 의지(물론 소속된 개발팀의 분위기)에 달린 것으로 보인다. 실전을 통해서 보다 부딪혀 봐야 겠다.