Mockito
🍸Mockito 이란?
테스트를 위한 Mock를 구축할 수 있게 해주는 자바 진영의 프레임워크
🧩Mockito Syntax
스프링 환경에서 코드를 작성했다. 스프링부트에서는 mock 객체를 라이브러리로 지원하기에 별다른 추가 빌드 코드 없이 사용 할 수 있다.
1. mock(className.class)
/**
* mock(classToMock.class)로 mock 객체를 만들 수 있다.
* 내부 구현은 동작하지 않고 0, null, false 등이 리턴된다.
*/
@Test
void createMockTest() {
// given
Cook mockCook = mock(Cook.class);
// when
mockCook.setOrderedItem("hamburger");
// then
assertThat(mockCook.getOrderedItem()).isEqualTo(null) ;
}
mock 객체를 만드는 함수로, mock 함수를 사용해 mock을 만들 경우 내부 구현은 동작하지 않고, 내부 메소드를 호출시 0,null, false등이 호출된다.
2. Verify(mockClass, ... ).methodName()
/**
* verify: mock 객체에 어떤 api가 호출되었는지, 몇 번 호출되었는지 확인 가능
*/
@Test
void verifyTest() {
Cook mockCook = mock(Cook.class);
mockCook.getOrderedItem();
// getOrderedItem이 한번이라도 호출되었는가?
verify(mockCook).getOrderedItem();
mockCook.getOrderedItem();
mockCook.getOrderedItem();
// getOrderedItem 총 3번 호출됨
verify(mockCook, times(3)).getOrderedItem();
// getOrderedItem 적어도 2번 이상 호출됨
verify(mockCook, atLeast(2)).getOrderedItem();
}
'methodName' 메소드가 몇 번이나 호출 되었는지 알 수 있도록 해주는 메소드다. mockClass 옆에 인자로 주어진 함수로 몇 번이나 호출되었는지 확인 할 수도 있다. times(n)는 n번 호출되었는지 확인하는 함수고, atLeast(n)는 n번 이상 호출되었는지 확인 할 수 있는 함수다.
3. When(mockMethodCall).thenReturn(something)
/**
* when: mock 객체가 어떤 값을 리턴하도록 만들 수 있음. 이것을 Stubbing이라고 한다.
*/
@Test
void whenTest() {
//given
Cook mockCook = mock(Cook.class);
// when
when(mockCook.getOrderedItem()).thenReturn("hamburger");
// then
assertThat(mockCook.getOrderedItem()).isEqualTo("hamburger");
}
When 함수를 사용해 특정한 값을 리턴하도록 적용 할 수 있다. 위의 코드에서 적용된 함수는 mockCook.getOrderedItem()이 호출되면 "hamburger"를 리턴하겠다는 뜻이다. 이를 stubbing이라고 한다.
4. ArgumentCaptor
/**
* ArgumentCaptor: Mock 객체에 전달된 인자를 확인하는 용도로 사용
*/
@Test
void argumentCaptorTest() {
Cook mockCook = mock(Cook.class);
ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
mockCook.setOrderedItem("pizza");
verify(mockCook).setOrderedItem(arg.capture());
assertThat(arg.getValue()).isEqualTo("pizza");
}
ArgumentCaptor를 사용해 mock 객체의 메소드에 어떤 인자들이 전달되었는지 확인 할 수 있다. 위의 코드를 보면 verify(mockCook).setOrderItem(arg.capture())라는 걸 볼 수 있는데, arg에 setOrderItem으로 전달되었던 데이터를 저장한다는 뜻이다.
5. Spy
/**
* Spying: Mock 객체는 내부 구현이 동작하지 않는것에 비해 Spy객체는 실제 객체처럼 동작하고
* Verify로 어떤 메소드가 호출되었는지 확인 할 수있다.
* 또한 When을 통해 일부 메소드를 stubbing 할 수 있다.
*/
@Test
void spyingCook() {
Cook spy = spy(Cook.class);
spy.setOrderedItem("hamburger");
assertThat(spy.getOrderedItem()).isEqualTo("hamburger");
}
mock 객체와는 다르게 실제 객체처럼 동작하는 클래스이다. 실제로 cook에 정의되어있는 set 메소드등이 정상적으로 작동하여 테스트를 통과하는 모습을 볼 수 있다.
⚠️ 주의사항
@Test
void spyingListError() {
ArrayList spyList = spy(ArrayList.class);
when(spyList.get(0)).thenReturn("apple");
when(spyList.get(1)).thenReturn("banana");
when(spyList.size()).thenReturn(10);
assertThat(spyList.get(0)).isEqualTo("apple");
assertThat(spyList.get(1)).isEqualTo("banana");
assertThat(spyList.size()).isEqualTo(10);
}
IndexOutOfBoundException 발생
spy는 실제 코드처럼 동작하기에 when라인의 get()함수를 사용 할 경우 저장되어 있는 값이 없어 에러를 발생 시키는 것을 볼 수 있다. 이는 다음과 같이 doReturn(value).when(spyObject).method() 패턴을 사용하면 고칠 수 있다.
@Test
void spyingListWhenFixed() {
ArrayList spyList = spy(ArrayList.class);
doReturn("apple").when(spyList).get(0);
doReturn("banana").when(spyList).get(1);
doReturn(10).when(spyList).size();
assertThat(spyList.get(0)).isEqualTo("apple");
assertThat(spyList.get(1)).isEqualTo("banana");
assertThat(spyList.size()).isEqualTo(10);
}
6. Exception Throw
/**
* 예외 발생 : when().thenThrow(ExceptionClass) 패턴 사용
*/
@Test
void whenGetUserInfoThrowException() {
Cook mockCook = mock(Cook.class);
when(mockCook.getOrderedItem()).thenThrow(NullPointerException.class);
mockCook.getOrderedItem();
}
when의 thenThrow(ExceptionClass) 를 활용해 예외를 발생시킬 수 있다.
NullPointerException이 발생한 것을 볼 수 있다.
발생한 예외를 테스트내에서 정상으로 표기하려면 다음과 같이 Junit5의 assertThrow를 사용하면 된다.
/**
* 예외 발생 : when().thenThrow(ExceptionClass) 패턴 사용
* Junit5 사용시 assertThrow(Exception.class, () -> { dosomething() } 사용
*/
@Test
void whenGetUserInfoThrowException() {
Cook mockCook = mock(Cook.class);
when(mockCook.getOrderedItem()).thenThrow(NullPointerException.class);
assertThrows(NullPointerException.class, () -> {
mockCook.getOrderedItem();
});
}
References
https://www.baeldung.com/mockito-annotations
https://codechacha.com/ko/mockito-best-practice/
Java - Mockito를 이용하여 테스트 코드 작성하는 방법
Mockito는 Java에서 인기있는 Mocking framework입니다. Mockito로 객체를 mocking하여 Unit Test를 작성할 수 있습니다. 직접 Mock 객체를 만들 수 있지만 Mockito와 같은 Mocking framework을 사용하면 번거로운 코드를
codechacha.com