728x90
Mock 객체란?
실제 객체를 만들기 위해서는 비용과 시간이 많이 들기 때문에, 비용이 적게 들도록 가짜 객체를 생성하여 사용하는데 이것이 Mock 객체이다.
일반적으로 테스트 더블은 상태(State)를 기반으로 테스트 케이스를 작성하는데
Mock 객체는 행위(behavior)를 기반으로 테스트 케이스를 작성한다
테스트 더블이란?
실제 객체를 사용하지 않고 이를 대신해줄 가짜 객체를 만들어 테스트 수행을 하는 방법
상태기반
- 특정한 메소드를 거친 후 객체의 상태에 대해 예상값을 비교
가장 보통의 예 : setName("test") → assertThat( "test", is(getName() );
행위기반
- 메소드 리턴값이 없는 void 이거나, 예상된 동작을 보장할 수 없을때
- 시나리오를 기반으로 A가 입력되었을 때, B가 입력되었을 때의 따른 판단
Mock은 언제 사용할까?
- 대부분의 경우 모듈의 '의존성'이 근본적인 원인이 되는데, 이 의존성을 단절 시키기 위해 사용된다
- 테스트 작성을 위한 환경 구축이 어려워서
- 테스트가 특정 경우나 순간에 의존적이라서
- 테스트 시간이 오래 걸려서
테스트 더블의 종류 (4가지 + Mock)
- Dummy Object
- 실제 기능까지는 필요하지 않고 인스턴스화된 객체만 필요할 때 사용한다
- 즉, 객체의 메소드 기능의 올바른 구현은 보장되어 있지 않다
- Test Stub (Dummy Object 업그레이드 버전)
- 더미 객체가 마치 실제로 동작하는 것처럼 보이게 만들어 놓은 객체다.
- 간단한 리턴값만 돌려준다.
- Fake Object (Test Stub의 업그레이드 버전)
- 여러 개의 인스턴스를 대표할 수 잇는 경우,
- 좀 더 복잡한 구현이 들어간 객체를 지칭한다.
- Test Spy
- 테스트 상황에 대해 감시해서 기록했다가 요청이 들어오면 보여주는 기능
Mock 프레임워크
Mock 프레임워크는 동적으로 Mock 객체를 만들어주는 프레임워크를 지칭한다.
Mock 프레임워크의 종류
- EasyMock
- jMock
- Mockito : 대표적
Mockito의 개발자 (Szczepan Faber)의 Mock 프레임워크의 정의
- 단순해야 한다.
- DSL로 만들지 말자. 복잡해진다.
- 문자열을 메소드 대신에 사용하지 말자
- 읽기 어려운 익명 내부 클래스를 사용하지 말자
- 리팩토링이 어려워서는 안된다.
기본 사용법 ( Mockito의 순서 )
- CreateMock : 인터페이스에 해당하는 Mock 객체를 만든다
- Stub : 테스트에 필요한 Mock 객체의 동작을 지정한다(단, 필요시에만)
- Exercise : 테스트 메소드 내에서 Mock 객체를 사용한다
- Verify : 메소드가 예상대로 호출됐는지 검증한다
import org.junit.Test;
import org.mockito.InOrder;
import java.util.LinkedList;
import java.util.List;
import static org.mockito.Mockito.*;
public class MockitoTest {
@Test
public void mockito(){
// 목 객체 생성
LinkedList mockedList = mock(LinkedList.class);
// 스텁 생성 ( 필요시 )
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
// 실행
System.out.println(mockedList.get(0));
//following throws runtime exception
//System.out.println(mockedList.get(1));
//following prints "null" because get(999) was not stubbed
//System.out.println(mockedList.get(999));
// 검증
// 한 번 호출 되었는가?
verify(mockedList, times(1)).get(0);
// 절대 호출되지 않았는가?
verify(mockedList, never()).get(999);
}
@Test
public void mockitoArgument(){
// 목 객체 생성
List mockedList = mock(List.class);
// 스텁 생성
when(mockedList.get(anyInt())).thenReturn("element");
// 실행
System.out.println(mockedList.get(1));
// get(1) 했을때 스텁에서 생성한 element를 리턴한다.
verify(mockedList).get(1);
}
@Test
public void mockitoVerifying(){
// 목 객체 생성
List mockedList = mock(List.class);
// 스텁 불필요
// 실행
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
// 검증
// 1번 추가했나?
verify(mockedList, times(1)).add("once");
// 2번 추가했나?
verify(mockedList, times(2)).add("twice");
// 3번 추가했나?
verify(mockedList, times(3)).add("three times");
// never happened를 절대 추가 안했나?
verify(mockedList, never()).add("never happened");
// 적어도 한번은 사용했나?
verify(mockedList, atLeastOnce()).add("three times");
// 적어도 2번은 사용 했나?
verify(mockedList, atLeast(2)).add("three times");
// 최대 5번 이상 호출 안되었나?
verify(mockedList, atMost(5)).add("three times");
}
@Test
public void mockitoVoid(){
// 목 객체 생성
List mockedList = mock(List.class);
// 스텁
// clear를 하면 RuntimeException()을 발생시킨다.
doThrow(new RuntimeException()).when(mockedList).clear();
// 실행
mockedList.clear();
}
@Test
public void mockitoOrder(){
// 목 객체 생성
List singleMock = mock(List.class);
// 실행
singleMock.add("was added first");
singleMock.add("was added second");
//싱글 목에 대한 정렬 객체 생성
InOrder inOrder = inOrder(singleMock);
// was added first가 첫번째로, 그리고 was added second가 두번째로 입력되었는지 검증한다.
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
}
}
728x90
'Programming > Backend' 카테고리의 다른 글
객체 지향, 오브젝트와 의존 관계의 이해 (0) | 2021.10.09 |
---|---|
API 명세서 뜯어보기 - StringTokenizer Class (0) | 2021.10.05 |
JUnit : 자바에서 사용하는 가장 대표적인 단위 테스트 프레임 워크 (0) | 2021.10.03 |
TDD : 테스트 주도 개발 (0) | 2021.10.03 |
API 명세서 뜯어보기 - Functional interface (0) | 2021.10.02 |