Mock: 진짜 객체와 비슷하게 동작하지만 프로그래머가 직접 그 객체의 행동을 관리하는 객체 (가짜 객체)
Mockito: Mock 객체를 쉽게 만들고 관리하고 검증할 수 있는 방법을 제공한다.
다음 세 가지만 안다면 Mock을 활용한 테스트를 쉽게 작성할 수 있다.
세 가지 위주로 살펴보도록 한다.
StudyService 클래스를 살펴보면 아래와 같이 다른 클래스들과 의존하고 있다. 따라서 인스턴스를 만드려고 한다면 MemberService, StudyRepository의 인스턴스가 필요하다.
이때, MemberService와 StudyRepository에 대해 Mocking이 필요하다.
Mock객체를 만드는 방법은 (1) 직접 mock 객체 생성, (2) Mock 애노테이션 사용 이 있다.
(1) 직접 mock 객체 생성
MemberService memberService = Mockito.mock(MemberService.class);
StudyRepository studyRepository = Mockito.mock(StudyRepository.class);
(2) Mock 애노테이션 사용
//필드로 빼서 사용
@ExtendWith(MockitoExtension.class)
class StudyServiceTest {
@Mock
MemberService memberService;
@Mock
StudyRepository studyRepository;
...
}
//메소드 매개변수로 사용
@Test
public void createStudyService(@Mock MemberService memberService,
@Mock StudyRepository studyRepository) throws Exception{
StudyService studyService = new StudyService(memberService,studyRepository);
assertNotNull(studyService);
}
모든 Mock 객체의 행동은
따라서, Mock객체를 호출할 때, 행동을 정의해줘야 한다.
(1) when
when(memberService.findById(1L)).thenReturn(Optional.of(member));
when(memberService.findById(2L)).thenThrow(new RuntimeException());
findById가 1L로 호출이 된다면, 특정 member가 호출되도록 정의한다. 혹은 예외를 던지도록 할 수 있다.
(2) doThrow
doThrow(new IllegalArgumentException()).when(memberService).validate(1L);
assertThrows(IllegalArgumentException.class, () -> {
memberService.validate(1L);
});
특정 매개변수를 받거나 호출된 경우, 예외를 발생시킬 수 있다. 또한 JUnit의 assertThrows를 통해 예외를 확인할 수 있다.
(3) 호출 횟수에 따라 다른 return 값 부여
when(memberService.findById(any()))
.thenReturn(Optional.of(member)) //member return
.thenThrow(new RuntimeException()) //예외 반환
.thenReturn(Optional.empty()); //empty 반환
verify(memberService, times(1)).validate(any()); //1번 호출
verify(memberService, never()).findById(any()); //한 번도 호출되지 않는다.
함수 호출 횟수를 검증할 수 있다.
BDD: 애플리케이션이 어떻게 행동해야 하는지에 대한 공통된 이해를 구성하는 방법
행동을 정의할 때, Given(주어진 상황에) / When(특정 행위를 한다면) / Then(이러한 결과가 나올 것이다.)과 같이 구성할 수 있다.
앞서 정의한 메서드를 BDD로 정의할 수 있다.
(1) when -> given
when(memberService.findById(1L)).thenReturn(Optional.of(member));
given(memberService.findById(1L)).willReturn(Optional.of(member));
(2) verify -> then
verify(memberService, times(1)).validate(any());
then(memberService).should(times(1)).validate(any());