Unit Test - Mockito란?
Mockito란?
- Unit Test를 위한 Java Mocking Framework
- 유닛 테스트에서 가짜 객체(Mock)를 지원해주는 Framework
- 구성
- Mock 객체 생성 - Mockito.mock() 메서드 (Static 메서드로 클래스명 생략가능)
- Mock 객체 동작 지정 - Stubbing이라고 하며 when().thenReturn() 방식
- Mock 객체 동작 수행확인 - verify()
- 공식홈페이지에서 자세히 확인가능 - [보러가기]
build.gradle 설정
2020.06.28 기준 최신버전 '1.9.5' - [최신버전 보러가기]
dependencies {
...
testImplementation group: 'org.mockito', name: 'mockito-all', version: '1.9.5'
}
Mock 객체 생성하기
mock() & @Mock & @InjectMocks 방법으로 Mock객체 생성
- @Mock
- mock 객체를 만들어서 반환
- @InjectMocs
- @Mock이나 @Spy 객체를 자신의 멤버 클래스와 일치하면 주입시켜서 mock객체 생성
// create mock Object
@Mock
Person person_annotation; // @Mock Annotation
Person person_method = mock(Person.class); // mock() Method
// using mock Object
person_annotation.setName("홍길동");
person_annotation.setAge(25);
// 검증(Verify) - Success
verfiy(person_annotation).setName("홍길동"); // setName("홍길동") 호출되었는지 검증
verfiy(person_annotation).setAge(25); // setAge(25) 호출되었는지 검증
// 검증(Verify) - Fail
verfiy(person_annotation).setAge(30); // setAge(30)은 호출한 적 없으므로 Fail
Stub
when() 메서드는 지정한 메서드를 호출할 경우 특정 값을 반환하도록 설정하는 메서드
// create Mock Object
@Mock
Person person;
// Stubbing
when(person.getName()).thenReturn("홍길동"); // getName() 호출 시 "홍길동" 반환하도록 설정
when(person.getAge()).thenReturn(25); // getAge() 호출 시 25 반환하도록 설정
when(person.getAdderss()).thenThrow(new RuntimeException()); // getAdderss() 호출 시 RuntimeException 예외 설정
System.out.println(person.getName()); // "홍길동"
System.out.println(person.getAge()); // 25
System.out.println(person.getAdderss()); // RuntimeException()
연속적인 Stubbing
when(mock.someMethod("some arg")) // 호출 대상 메서드
.thenThrow(new RuntimeException()) // 첫번째 반환 값 설정
.thenReturn("Test"); // 두번째 반환 값 설정
// 첫번째 호출 : throws runtime exception 발생
mock.someMethod("some arg");
// 두번째 호출 : "Test" 반환
System.out.println(mock.someMethod("some arg"));
// 호출한 순서대로 결과값을 Return : One, Two, Three
when(mock.someMethod("some arg"))
.thenReturn("One", "Two", "Three");
doThrow
doThrow() 메서드를 사용하면 해당 구문 호출 시 예외를 발생하도록 설정이 가능
@Test
public void mockTest() {
// mock() 메서드로 mock 객체 생성
Person person = mock(Person.class);
// doThrow() 설정, person.setAge(28) 호출 시 RuntimeException 예외 발생
doThrow(new RuntimeException())
.when(person) // 협력객체(대상) 설정
.setAge(eq(28)); // eq는 정확한 값을 의미, eq(28)은 정확한 28을 의미
person.setAge(30); // success
person.setAge(25); // success
person.setAge(27); // success
person.setAge(28); // fail, 예외(RuntimeException) 발생
}
doThrow() - 발생시킬 예외 설정
when() - 협력객체(대상) 설정
setAge(eq(28)) - 예외 발생시킬 메서드 설정, 해당 메서드에 28값을 선언 시 예외발생
verify
verify()는 해당 구문이 호출 되었는지를 검증하는 메서드
단순하게 해당 구문 호출 확인뿐만 아니라 호출 횟수 / 타임아웃 시간까지 지정해서 검증이 가능한 메서드
@Test
public void mockTest() {
Person person = mock(Person.class);
person.setName("홍길동");
person.setAge(20);
person.setAge(21);
person.setAddress("서울");
person.setAddress("경기도");
person.setAddress("인천");
// times() - n번 호출했는지 체크, times(1)은 Default값
verfiy(person, times(1)).setName(any(String.class));
verfiy(person).setName(any(String.class));
// 2번, 3번횟수도 검증가능
verify(person. times(2)).setAge(anyInt());
verfiy(person, times(3)).setAddress(any(String.class));
// never() - 한번도 호출하지 않았는지 체크, ::= times(0) 의미
verify(person, never()).getName(); // Success, getName() 0번 호출 확인
verify(person, never()).setName(eq("홍길동")); // Success, eq()는 정확하게 해당값을 의미
verify(person, never()).setName(eq("최루피")); // Fail, setName("최루피") 호출했으므로 Fail
// 적어도 한번, 적어도 x번, 최대 x번
verify(person, atLeastOnce()).setName(any(String.class)); // Success, 적어도 한번
verify(person, atLeast(2)).setName(any(String.class)); // Fail, 적어도 2번
verify(person, atMost(2)).setName(any(String.class)); // Success, 최대 2번
// 지정된 시간(Millis) 안으로 메소드가 종료 했는지?
verify(person, timeOut(100)).setName(any(String.class)); // Success
// 지정된 시간(Millis) 안으로 1번 이상 메서드 호출 했는지?
verify(person, timeOut(100).atLeast(1)).setName(any(String.class)); // Success
}
아무일이 일어나지 않는 mock 검증
List mock_one = mock(List.class);
List mock_two = mock(List.class);
List mock_three = mock(List.class);
mock_one.add("One");
// mock_one에 대한 호출 메서드 검증
verify(mock_one).add("One");
verify(mock_one, never()).add("Two");
// 나머지 Mock들이 아무 일도 발생하지 않았는지 검증
verifyZeroInteractions(mock_two, mock_three);
Mockito 메서드 정리
- mock() - Mock 객체(가짜 객체) 생성 메서드
- when() - 협력객체 메서드 반환 값을 지정해주는 역할 stub)
- verify() - Stub 내부 협력객체 메서드가 호출 되었는지 여부 확인
- times() - 지정한 횟수 만큼 협력 객체 메서드가 호출 되었는지 확인
- never() - 한번도 호출되지 않았는지 여부 검증
- atLeastOnce() - 최소 1번은 호출되었는지 확인
- atLeast() - 최소 지정한 횟수만큼 호출되었는지 확인
- atMost() - 최대 지정한 횟수만큼 호출되었는지 확인
- clear() - Stub 초기화
- timeOut() - 지정된 시간안에 호출되었는지 확인
Annotation 정리
- @Test
: 테스트 대상 메서드임을 의미, @Before가 완료되면 실제 코드 테스트를 진행 - @Before
: @Test 시작하기 전에 호출 되어서 사전작업을 수행, @Test 시작 전 항상 호출 - @After
: @Test가 종료되면 호출, 메모리에서 resource를 release 작업, @Before/@After는 @Test 실행전, 후에 실행 - @Rule
: 해당 Test 클래스에서 사용하게 될 ActvitiyTestRule과 ServiceTestRule에 대해서 정의 - @BeforeClass / @AfterClass
: public static method로 정의해야 하며, @Before와 @After와 달리 테스트 클래스에서 딱 한번씩만 수
@Before / @After는 각각의 @Test 실행 전, 후마다 실행된다는 점이 다름 - @Test(timeout=2000)
: @Test 룰에 대한 Timeout 지정, 설정한 timeout 시간 내에 테스트가 완료하지 않으면 Fail (Milliseconds 단위)