개발
Mockito를 이용한 Unit테스트 Mocking
ka0oll
2020. 3. 22. 15:24
Mockito
mockito란?
- mock객체를 생성하여 stubbing하고 검증 할수 있도록 한다.
용어 정리
Stub : 테스트에서 호출된 요청에 대한 미리 준비해둔 결과를 제공
Mock : stub + 행동에 대한 검증을 위한것
Spy : 실제 구현체 + mock, 실제 구현체의 일부만 mocking 할수있는 기능 제공
시작하기
스프링 부트 2.2+ 프로젝트 생성시 spring-boot-starter-test에서 자동으로 Mockito 추가해 줌.
스프링 부트 쓰지 않는다면, 의존성 직접 추가.
<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 생성
직접 생성
MemberService memberService = mock(MemberService.class);
StudyRepository studyRepository = mock(StudyRepository.class);
@Mock 애노테이션으로 만드는 방법
@ExtendWith(MockitoExtension.class)
class StudyServiceTest {
@Mock MemberService memberService;
@Mock StudyRepository studyRepository;
}
@ExtendWith(MockitoExtension.class)
class StudyServiceTest {
@Test
void createStudyService(@Mock MemberService memberService,
@Mock StudyRepository studyRepository) {
StudyService studyService = new StudyService(memberService, studyRepository);
assertNotNull(studyService);
}
}
Mock객체 stubing
기본 행동
- null리턴
- primitive 기본값 리턴
- 콜렉션은 비어있는 콜렉션
- Void는 아무 액션이 없다.
조작
//You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);
//stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
//stubbing using built-in anyInt() argument matcher
when(mockedList.get(anyInt())).thenReturn("element");
Mock객체 검증
호출 검증
//using mock
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
//following two verifications work exactly the same - times(1) is used by default
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
//exact number of invocations verification
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");
//verification using never(). never() is an alias to times(0)
verify(mockedList, never()).add("never happened");
//verification using atLeast()/atMost()
verify(mockedList, atMostOnce()).add("once");
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("three times");
verify(mockedList, atMost(5)).add("three times");
순서 검증
// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);
//using a single mock
singleMock.add("was added first");
singleMock.add("was added second");
//create an inOrder verifier for a single mock
InOrder inOrder = inOrder(singleMock);
//following will make sure that add is first called with "was added first", then with "was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);
//using mocks
firstMock.add("was called first");
secondMock.add("was called second");
//create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = inOrder(firstMock, secondMock);
//following will make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
// Oh, and A + B can be mixed together at will
특정 시간 이내에 호출됐는지
Verification with timeout https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#verification_timeout
Mockito BDD style
BDD : Behaviour-Driven Development의 약자, 어플리케이션이 어떻게 "행동"해야 하는지에 대한 이해로, TDD에서 창안됨
행동에 대한 스펙
- Title
- Narrative : As a / I want / so that
- Acceptance criteria : Given / When / Then
Mockito는 BDD에 대한 스펙 제공
- 기존에 stubbing 할때 when은 BDD와 맞지 않음, BDD에서는 given임
When -> Given given(memberService.findById(1L)).willReturn(Optional.of(member)); given(studyRepository.save(study)).willReturn(study);
Verify -> Then
then(memberService).should(times(1)).notify(study);
then(memberService).shouldHaveNoMoreInteractions();