-
MockitoBackEnd/Mockito 2022. 2. 11. 07:44반응형
Mock이란 진짜 객체와 비슷하게 동작하지만 프로그래머가 직접 그 객체의 행동을 관리하는 객체입니다. 이러한 Mock 객체를 쉽게 만들고 관리하며 검증할 수 있는 방법을 제공하는 프레임워크가 Mockito입니다. 외부 API 통신이나 데이터베이스에 접근하는 Service, DAO, Repository가 구현되지 않은 상황에서 Mock 객체를 사용하여 개발을 진행할 수 있습니다(TDD, Test-driven development). Mock을 활용한 테스트 작성 시 1) Mock을 만드는 방법, 2) Mock이 어떻게 동작해야 하는지 관리하는 방법, 3) Mock의 행동을 검증하는 방법만 고려하면 됩니다.
의존성 추가
Spring boot 2.2+ 프로젝트 생성 시 spring-boot-starter-test에서 자동으로 Mockito 의존성을 추가해 줍니다. 의존성을 직접 추가해야 한다면 아래 dependency를 추가해주면 됩니다.
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.1.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>3.1.0</version> <scope>test</scope> </dependency>
Mock 객체 만들기
1. Mockito.mock() 메소드로 만드는 방법
MemberService memberService = Mockito.mock(MemberService.class); StudyRepository studyRepository = Mockito.mock(StudyRepository.class);
2. @Mock 애노테이션으로 만드는 방법
JUnit 5 extension으로 MockitoExtension을 사용해야 합니다.
// Field @ExtendWith(MockitoExtension.class) class StudyServiceTest { @Mock MemberService memberService; @Mock StudyRepository studyRepository; @Test void createStudyService() { StudyService studyService = new StudyService(memberService, studyRepository); assertNotNull(studyService); } } // Method 매개변수 @ExtendWith(MockitoExtension.class) class StudyServiceTest { @Test void createStudyService(@Mock MemberService memberService, @Mock StudyRepository studyRepository) { StudyService studyService = new StudyService(memberService, studyRepository); assertNotNull(studyService); } }
Mock 객체의 행동
- 기본적으로 모든 리턴 값이 존재하는 메소드는 Null을 리턴합니다. (Optional 타입은 Optional.empty을 리턴합니다.)
- Primitive 타입은 기본 Primitive 값을 리턴합니다. (Boolean인 경우 false, Integer, Long의 경우 0 등)
- 콜렉션은 비어있는 콜렉션을 리턴합니다.
- void 메소드의 경우 예외를 던지지 않고 아무런 일도 발생하지 않습니다.
Mock 객체 Stubbing
Stubbing이란 Mock 객체의 행동을 조작하는 것입니다. Mock 객체를 조작해서 특정한 매개변수를 받은 경우 특정한 값을 리턴하거나 예외를 던지도록 할 수 있습니다.
@Test void stubbingTest() { // member 정의 Member member = new Member(); member.setId(1L); member.setEmail("hello@mockito.com"); // 특정한 매개변수 1L을 받은 경우 앞서 정의한 member를 반환합니다. when(memberService.findById(1L)).thenReturn(Optional.of(member)); // 특정한 매개변수 1L을 받은 경우 예외를 던집니다. // when(memberService.findById(1L)).thenThrow(RuntimeException.class); // 결과를 확인합니다. Optional<Member> findMember = memberService.findById(1L); assertEquals("hello@mockito.com", findMember.get().getEmail()); }
when 구문에서 1L이 아닌 다른 값을 넣으면 테스트에 실패합니다. 이러한 파라미터를 Argument matchers를 통해 범용적으로 사용할 수 있습니다.
when(memberService.findById(any())).thenReturn(Optional.of(member));
void 메소드의 경우 특정한 매개변수를 받거나 호출된 경우 예외를 발생시킬 수 있습니다.
@Test void stubbingVoidTest() { doThrow(new IllegalArgumentException()).when(memberService).validate(1L); assertThrows(IllegalArgumentException.class, () -> { memberService.validate(1L); }); }
메소드가 동일한 매개변수로 여러번 호출될 때 각기 다르게 행동하도록 조작할 수도 있습니다.
@Test void stubbingTest2() { // member 정의 Member member = new Member(); member.setId(1L); member.setEmail("hello@mockito.com"); /** memberService의 findById 메소드가 * 처음 호출되면 정상적으로 정의한 member를 반환합니다. * 두번째 호출되면 RuntimeException이 발생합니다. * 세번째 호출되면 빈 값을 반환합니다. */ when(memberService.findById(any())) .thenReturn(Optional.of(member)) .thenThrow(new RuntimeException()) .thenReturn(Optional.empty()); // 결과를 확인합니다. Optional<Member> findMember = memberService.findById(1L); assertEquals("hello@mockito.com", findMember.get().getEmail()); assertThrows(RuntimeException.class, () -> { memberService.findById(10L); }); assertEquals(Optional.empty(), memberService.findById(100L)); }
Mock 객체 확인
Mock 객체가 어떻게 사용이 되었는지 확인할 수 있습니다. 특정 메소드가 특정 매개변수로 몇 번 호출 되었는지, 어떤 순서대로 호출했는지, 특정 시간 이내에 호출되었는지, 특정 시점 이후 아무 일도 벌어지지 않았는지 등을 알 수 있습니다.
@Test void verifyTest() { StudyService studyService = new StudyService(memberService, studyRepository); // member 정의 Member member = new Member(); member.setId(1L); member.setEmail("hello@mockito.com"); // study 정의 Study study = new Study(1, "Java"); when(memberService.findById(1L)).thenReturn(Optional.of(member)); when(studyRepository.save(study)).thenReturn(study); studyService.createNewStudy(1L, study); // study 매개변수로 1번 호출되었는지 확인합니다. verify(memberService, times(1)).notify(study); // notify 메소드 호출 이후 아무 일도 하지 않았는지 확인합니다. verifyNoMoreInteractions(memberService); // study 호출 이후 member로 호출 되었는지 순서를 확인합니다. /* InOrder inOrder = inOrder(memberService); inOrder.verify(memberService).notify(study); inOrder.verify(memberService).notify(member); */ }
반응형'BackEnd > Mockito' 카테고리의 다른 글
Mockito BDD 스타일 API (0) 2022.02.11