Mockito @Mock — Answer

Circlee7
5 min readOct 26, 2020

mockito 는 mocking framework 이다

mock 이란?

모의 객체(Mock Object)란 주로 객체 지향 프로그래밍으로 개발한 프로그램을 테스트 할 경우 테스트를 수행할 모듈과 연결되는 외부의 다른 서비스나 모듈들을 실제 사용하는 모듈을 사용하지 않고 실제의 모듈을 "흉내"내는 "가짜" 모듈을 작성하여 테스트의 효용성을 높이는데 사용하는 객체이다. 사용자 인터페이스(UI)나 데이터베이스 테스트 등과 같이 자동화된 테스트를 수행하기 어려운 때 널리 사용된다.- 위키백과

잘 설명되어있는 글이 많이 있다.

https://velog.io/@june0313/Mockito-Mock-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%A3%BC%EC%9E%85%ED%95%98%EA%B3%A0-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%98%EA%B8%B0

Mockito mock 사용 경험을 기록한다.
기록용이니 Mock Answer 에 대해 더 잘 설명된 글을 찾아보시는게 좋겠어요. ㅜㅜ

나는 WebClient.Builder 를 의존하는 컴포넌트를 테스트 하려했다.

@Mock
WebClient.Builder webClientBuilderMock;
@Mock
private WebClient webClientMock;

Webclient.Builder 의 build() 메소드 실핼에 대한 리턴도 WebClient mock 객체로 지정했다.

when(webClientBuilderMock.build()).thenReturn(webClientMock);

그리고 테스트!

당연히 Error가 발생했다. WebClient.Builder 의 defaultHeader 라든지 여타 다른 빌더 메소드는 목킹하지 않았기 때문이다.

여기서 부터 삽질여정. ( pass 해도 됩니다.)

빌더내 모든 메소드를 목킹하기 너무나 귀찬다고 생각했고.

목킹하지 않은 메소드외에는 실제 인스턴스의 메소드를 사용할수있는 @Spy 로 변경하면 되겟다는 생각을 했다. ← 왜 이런생각을…

WebClient.build() 의 기본 리턴은 DefaultWebClientBuilder 인데 final class 라서 상속을 기반한 @Spy 로는 동작할수 없었다.

WebClient.Builder 상속받은 custom builder 를 만들고 DefaultWebClientBuilder 를 delegate 했다.

무언가 홀린듯 의식의 흐름처럼…

다시 테스트!

역시 제대로 이루어질리 없다. build() 메소드는 정상적으로 WebClient 인스턴스를 리턴했지만.

지정했던 webClientMock 객체가 아니었다.

작성한 custom builder 의 build() 가 아닌 다른 메소드를 거치며 내부 delegate 했던 DefaultWebClientBuilder 로 바뀌었고.

당연히 build() 메소드가 실행되는 주체는 DefaultWebClientBuilder 였다.

삽질여정 끝.

Builder 클래스는 build() 메소드 외에는 자기 자신 self 인스턴스를 리턴한다.

자기 자신객체를 리턴하는 모든 메소드에 대해 mocking 을 편하게 지정하려면 아래와 같이 타켓 클래스에 대해 Answer를 지정 하면된다.

mock(Class<T>, Answer);

Answer 구현체는 mockito 패키지 내에 이미 작성되어있는 TriesToReturnSelf 를 사용하면 되겟다. (물론 직접 작성해도 된다.)

public class TriesToReturnSelf implements Answer<Object>, Serializable{private final ReturnsEmptyValues defaultReturn = new ReturnsEmptyValues();public Object answer(InvocationOnMock invocation) throws Throwable {
Class<?> methodReturnType = invocation.getMethod().getReturnType();
Object mock = invocation.getMock();
Class<?> mockType = MockUtil.getMockHandler(mock).getMockSettings().getTypeToMock();
if (methodReturnType.isAssignableFrom(mockType)) {
return invocation.getMock();
}
return defaultReturn.returnValueFor(methodReturnType);
}
}

@Mock 어노테이션 기반으로 작성해도 answer attribute 를 지정할수 있고

미리 정의된 answer 중 Answer.RETURNS_SELF 를 사용하면 된다.
기본값은 RETURNS_DEFAULTSGloballyConfiguredAnswer 를 사용한다.

Answer.RETURNS_SELF 는 위의 코드 얘로 보여준 TriesToReturnSelf 를 사용한다.

다음과 같은 주석도 친절하게 써있다. 
“An answer that tries to return itself. This is useful for mocking Builders.”

다시 WebClient.Builder 의존성 객체 정의하는 부분을 다음과 같이 수정했다.

@Mock(answer = Answers.RETURNS_SELF)
private WebClient.Builder webClientBuilderMock;

다시 테스트!

build() 메소드 수행시 의도했던 webClientMock 인스턴스가 리턴되었다.

.
.
org.mockito.internal.stubbing.defaultanswers 패키지에 정의된 Answer 들에 대해선 , 다시 추가 작성하겠다.

--

--