부록 C. Mockito 기초 사용법
테스트 주도 개발 시작하기 부록 C를 요약한 내용입니다.
Mockito는 모의 객체 생성, 검증, 스텁을 지원하는 프레임워크이다. Mockito의 기본 사용법을 설명하며 이를 통해 대역을 보다 원활하게 사용할 수 있게 될 것이다.
모의 객체 생성
Mockito.mock() 메서드를 이용하면 특정 타입의 모의 객체를 생성할 수 있다.
@Test
void mockClass() {
GameNumGen mockGen = mock(GameNumGen.class);
given(mockGen.generate(GameLevel.EASY)).willReturn("123");
assertEquals("123", mockGen.generate(GameLevel.EASY));
}
스텁 설정
모의 객체를 생성한 뒤에는 BDDMockito 클래스를 이용해서 모의 객체에 스텁을 구성할 수 있다.
@Test
void mockClass() {
GameNumGen mockGen = mock(GameNumGen.class);
given(mockGen.generate(GameLevel.EASY)).willReturn("123");
assertEquals("123", mockGen.generate(GameLevel.EASY));
}
지정한 값을 리턴하는 대신에 익셉션을 발생하게 설정할 수도 있다.
@Test
void mockThrowTest() {
GameNumGen genMock = mock(GameNumGen.class);
given(genMock.generate(null)).willThrow(new IllegalArgumentException());
assertThrows(
IllegalArgumentException.class,
() -> genMock.generate(null));
}
인자 매칭 처리
org.mockito.ArgumentMatchers 클래스를 사용하면 정확하게 일치하는 값 대신 임의의 값에 일치하도록 설정할 수 있다.
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
public class VerifyTest {
@Test
void anyMatchTest() {
GameNumGen genMock = mock(GameNumGen.class);
given(genMock.generate(any())).willReturn("456");
String num = genMock.generate(GameLevel.EASY);
then(genMock).should().generate(GameLevel.EASY);
}
}
ArgumentMatchers 클래스는 any() 외에도 다음의 메서드를 제공한다.
anyInt(), anyShort(), anyLong(), anyByte(), anyChar(). anyDouble(), anyFloat(), anyBoolean() : 기본 데이터 타입에 대한 임의 값 일피
anyString() : 문자열에 대한 임의 값 일치
anyList(), antSet(), anyMap(), anyCollection() : 임의 콜렉션에 대한 일치
matches(String), matches(Pattern) : 정규표현식을 이용한 String 값 일치 여부
행위 검증
모의 객체의 역할 중 하나는 실제로 모의 객체가 불렸는지 검증하는 것이다.
public class VerifyTest {
@Test
void anyMatchTest() {
GameNumGen genMock = mock(GameNumGen.class);
given(genMock.generate(any())).willReturn("456");
String num = genMock.generate(GameLevel.EASY);
then(genMock).should().generate(GameLevel.EASY);
}
}
정확하게 한 번만 호출된 것을 검증하고 싶다면 should() 메서드에 Mockito.only()를 인자로 전달한다.
then(genMock).should(only()).generate(GameLevel.EASY);
atLeast(int) : 적어도 지정한 횟수만큼 호출
atLeastOnce() : atLeast(1)과 동일
atMost(int) : 최대 지정한 횟수만큼 호출
인자 캡쳐
단위 테스트를 실행하다보면 모의 객체를 호출할 때 사용한 인자를 검증해야 할 때가 있다. String이나 int와 같은 타입은 쉽게 검증할 수 있지만 많은 속성을 가진 객체는 쉽게 검증하기 어렵다. 이럴 때 사용할 수 있는 것이 인자 캡처이다.
Mockito의 ArgumentCaptor를 사용하면 메서드 호출 여부를 검증하는 과정에서 실제 호출할때 전달한 인자를 보관할 수 있다.
@DisplayName("가입하면 메일을 전송함")
@Test
void whenRegisterThenSendMail() {
userRegister.register("id", "pw", "email@email.com");
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
BDDMockito.then(mockEmailNotifier).should().sendRegisterEmail(captor.capture());
String realEmail = captor.getValue();
assertEquals("email@email.com", realEmail);
}
JUnit 5 확장 설정
Mockito의 JUnit 5의 확장 기능을 사용하면 애노테이션을 이용해서 모의 객체를 생성할 수 있다. 확장 기능을 사용하려면 mockito-junit-jupiter 의존을 추가해야 한다.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
의존을 추가했다면 MockitoExtension 확장을 사용할 수 있다.
@ExtendWith(MockitoExtension.class)
public class JUnit5ExtensionTest {
@Mock
private GameNumGen genMock;
...
}