반응형
Mockito란?
- Java에서의 단위 테스트를 지원하기 위한 Mocking Framework
- Mock(가짜) 객체를 통해 테스트 코드 작성을 더 원활하게 해줌
설치 방법
Maven
jar파일 다운로드 혹은 직접 설치
Gradle
repositories { mavenCentral() }
dependencies { testImplementation "org.mockito:mockito-core:3.+" }
Mock 사용 예제
호출 되었는지 확인하기
import static org.mockito.Mockito.*;
class TempTest {
@Test
void verify1() {
List mockedList = mock(List.class);
mockedList.add("one");
verify(mockedList).add("one");
}
@Test
void verify2() {
List mockedList = mock(List.class);
mockedList.add("one");
verify(mockedList).add("two");
}
}
- mock()을 이용해 List.class 형태의 가상의 객체를 생성
- verify()를 통해 메소드가 호출되었는지 검증
verify1()의 경우 mockedList.add("one")이 호출되었으므로 테스트가 통과했지만,
verify2()의 경우 mockedList.add("two")가 호출되지 않았으므로 테스트가 실패했다.
Stub 처리하기
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class MockTest {
@Test
void test() {
LinkedList mockedList = mock(LinkedList.class);
when(mockedList.get(0)).thenReturn("first");
assertEquals("first", mockedList.get(0));
assertNotEquals("first", mockedList.get(999));
assertEquals(null, mockedList.get(999));
}
}
- mock()을 이용해 LinkedList.class 형태의 가상의 객체를 생성
(mockStatic()을 통해 static 메소드도 처리 가능) - when()과 thenReturn()을 통해 특정 코드가 실행되면 원하는 결과를 받을 수 있도록 설정
위 예제의 같은 경우에는 mockedList에서 index 0번을 가져올 때 "first"라는 문자열을 받을 수 있도록 했다.
index 0 외에는 값을 설정하지 않아서 null을 가져오게 된다.
when(mockedList.get(1)).thenThrow(new RuntimeException());
- thenThrow()를 통해 특정 코드가 실행되면 원하는 Exception을 발생시킬 수도 있다.
@Test
void stub_test() {
List mockedList = mock(List.class);
when(mockedList.size())
.thenReturn(1,2,3,4,5);
assertEquals(mockedList.size(),1);
assertEquals(mockedList.size(),2);
assertEquals(mockedList.size(),3);
assertEquals(mockedList.size(),4);
assertEquals(mockedList.size(),5);
}
위와 같이 thenReturn 값을 여러개 넣을 수도 있다.
호출 횟수 검증하기
@Test
void verify_test() {
LinkedList mockedList = mock(LinkedList.class);
mockedList.add("a");
mockedList.add("a");
mockedList.add("a");
mockedList.add("b");
verify(mockedList, times(3)).add("a");
verify(mockedList, never()).add("c");
verify(mockedList, atMostOnce()).add("b");
verify(mockedList, atLeastOnce()).add("a");
verify(mockedList, atLeast(2)).add("a");
verify(mockedList, atMost(5)).add("a");
}
- times(n): n번 호출했음
- never(): 한 번도 호출하지 않음
- atMostOnce(): 최대 한번 호출
- atLeastOnce(): 최소 한번 호출
- atLeast(n): 최소 n번 호출
- atMost(n): 최대 n번 호출
호출 순서 확인하기
@Test
void verify_test() {
List singleMock = mock(List.class);
singleMock.add("first");
singleMock.add("second");
InOrder inOrder = inOrder(singleMock);
inOrder.verify(singleMock).add("first");
inOrder.verify(singleMock).add("second");
}
- inOrder(객체)를 통해 InOrder 객체 생성
추가적인 interaction 확인하기
@Test
void test1() {
List mockedList = mock(List.class);
mockedList.add("one");
mockedList.size();
verify(mockedList).add("one");
verifyNoMoreInteractions(mockedList);
}
@Test
void test2() {
List mockedList = mock(List.class);
mockedList.add("one");
verify(mockedList).add("one");
verifyNoMoreInteractions(mockedList);
}
- verifyNoMoreInteractions()
: verify() 후에 추가적인 interaction이 발생했는지 확인
- 다만 위 메소드는 테스트 메소드마다 사용하기는 부적절하고 과도하게 남용하면 유지 관리하기 어려운 테스트가 됨
- never()를 이용하는게 더 명확하고 의도를 잘 전달할 수 있다.
test1()의 경우에는 add("one") 이후로 mock 객체의 size() 메소드를 이용했기 때문에 실패한다.
test2()의 경우에는 add("one") 이후로 mock 객체를 사용하지 않았기 때문에 성공한다.
@Mock 어노테이션 사용하기
@Mock private User user;
- mock() 외에도 @Mock을 이용해 가상의 객체를 생성할 수 있음
콜백과 함께 Stub 처리하기
@Test
void test1() {
List<Integer> list = mock(List.class);
when(list.size()).thenAnswer(
new Answer<Integer>() {
private int cnt = 3;
public Integer answer(InvocationOnMock invocation) {
return new Integer(++cnt);
}
});
System.out.println(list.size());
}
@Test
void test2() {
List<Integer> list = mock(List.class);
when(list.get(1)).thenAnswer(
new Answer<Integer>() {
public Integer answer(InvocationOnMock invocation) {
Object[] arguments = invocation.getArguments();
return (Integer) arguments[0];
}
});
System.out.println(list.get(1));
}
- thenAnswer()와 Answer 인터페이스를 구현해 직접 동작 방식을 구현할 수 있음
- arguments[0, 1, ... n]을 통해 입력받은 파라미터를 사용할 수 있음
위 예제의 경우 구동이 된다는 것을 보여주기 위한 코드로 적절한 테스트 코드는 아님
test1() 결과: 4, test2() 결과: 1
객체를 선택적으로 stub하기
@Test
void test1() {
List list = new LinkedList();
List spy = spy(list);
// stub
when(spy.size()).thenReturn(100);
// real 메소드
spy.add("one");
spy.add("two");
// stub된 메소드
System.out.println(spy.size());
// real 메소드
System.out.println(spy.get(0));
verify(spy).add("one");
}
- spy() 또는 @Spy를 통해 스파이 객체로 생성
- 일부 메소드만 stub하고 나머지 메소드들은 정상 동작하도록 하고 싶을 때 작성
when(spy.get(3)).thenReturn("foo");
위와 같은 코드는 불가능하다. (get()은 real 메소드이고 spy의 3번째 값이 존재하지 않으므로)
아래와 같이 doReturn(), Answer, Throw() 등을 사용하자.
doReturn("foo").when(spy).get(0);
참고
반응형
'Develop > Java' 카테고리의 다른 글
[Java] Enum에 대해 (4) | 2021.12.13 |
---|---|
[Java] org.opentest4j.AssertionFailedError: Expected ... to be thrown, but nothing was thrown. 에러 (0) | 2021.12.07 |
[Java] java.lang.UnsupportedOperationException (1) | 2021.11.21 |
Comparator와 Comparable 정리 (0) | 2021.10.21 |
[Java] BufferedReader, BufferedWriter (0) | 2021.10.04 |