Spring 기반의 웹 어플리케이션 개발 시 테스트를 도와주는 도구이다. 테스트하고 싶은 객체가 다른 객체를 주입받아 동작한다면 테스트가 어려울 수 있다. 개발자가 원하는 객체에 관한 테스트만 진행할 수 있도록 의존성을 갖고 있는 다른 객체를 가짜 객체 로 대체해여 테스트할 수 있도록 도와준다.
Mockito 에서 가짜 객체의 의존성 주입 을 위해서는 크게 3가지 어노테이션이 사용된다.
@Mock
: Mock 객체를 만들어 반환해주는 어노테이션@Spy
: Stub 하지 않는 메서드들은 원본 메서드 그대로 사용하는 어노테이션@InjectMocks
: @Mock
또는 @Spy
로 생성된 가짜 객체를 자동으로 주입시켜주는 어노테이션예를 들어, controller 에 대한 단위테스트를 진행한다고 하자. controller 에서 service 를 주입받는다면 service 를 @Mock 을 이용해 가짜 객체로 생성하고 @InjectMocks 를 통해 controller 에 주입시킨다.
Mockito 도 테스팅 프레임워크이기 때문에 JUnit
과 결합되기 위해서는 별도의 작업이 필요하다. 기존의 JUnit4
에서는 @RunWith(MockitoJUnitRunner.class)
를 붙여 연동시켰다. 하지만 SpringBoot 2.2.0 부터 공식적으로 JUnit5 를 지원하여 @ExtendWith(MockitoExtension.class)
를 붙여야 연동이 가능하다.
@DisplayName("지하철 노선 관련 service 테스트")
@ExtendWith(MockitoExtension.class)
class LineServiceTest {
private static final Line LINE = new Line("신분당선", "bg-red-600");
@Mock
private LineDao lineDao;
@InjectMocks
private LineService lineService;
// 생략
}
위의 예시 코드는 우테코 지하철 노선도 미션에서 지하철 노선 Service 테스트의 일부 코드이다. Service 계층에서 Dao 를 주입받아 사용하므로 가짜 객체로 StationDao 를 생성했고, 그 가짜 객체를 StationService 에 주입시켰다. Mockito 라이브러리를 사용하기 위해서 @ExtendWith(MockitoExtension.class)
도 선언해주었다.
@DisplayName("지하철 노선을 생성한다.")
@Test
void save() {
// when
long lineId = lineService.save(LINE);
// mocking
given(lineDao.find(any()))
.willReturn(Optional.of(LINE));
// then
assertThat(lineService.find(lineId)).isEqualTo(lineService.find(lineId));
}
가짜 객체가 예상대로 올바르게 동작하기 위해서는 설정이 필요하다. 위의 코드는 지하철 노선도 정보를 생성하는 테스트코드이다. Service 객체를 이용하여 save() 메서드를 호출한다. 실제로 DB 와 연결되어 있지 않고 Dao 는 가짜 객체이므로 테스트가 실패할 것이다. lineService 의 find() 는 lineDao 의 find() 를 호출하므로 정상동작할 경우의 결과를 미리 설정해두었다.
https://mangkyu.tistory.com/145
PotentialStubbingProblem 예외 발생!
// mockito 라이브러리의 권장 방식
doReturn(false)
.when(stationDao).existStationById(1L);
// mockito 라이브러리의 비권장 방식
given(stationDao.existStationById(1L))
.willReturn(false);
원인은 순차적으로 같은 메서드를 호출할 때, 값에 따라 결과가 달라졌어야 하는데 하나의 경우에만 설정하여 오류남 그러면서 mockito 라이브러리가 권장하는 방식을 찾았음