Mockito는 Java에서 인기있는 Mocking framework입니다.
이 글에서는 Mockito의 Annotation, @Mock
, @Spy
, @Captor
, @InjectMocks
를 사용하는 방법에 대해서 알아봅니다.
Annotation을 사용하기 위한 설정
Mockito 라이브러리에서 @Mock
등의 Annotation들을 사용하려면 설정이 필요합니다.
만약 이런 설정 없이 @Mock
등을 사용한다면 NullPointerException이 발생합니다.
아래에서 소개하는 방법 중 하나를 선택하셔서 테스트 코드에 적용하시면 됩니다.
1. MockitoJUnit.rule
다음과 같이 @Rule
을 정의하시면 @Mock
등의 Annotation을 사용할 수 있습니다.
가능하다면 이 방법을 사용하시는 것이 좋습니다.
public class MockitoTest {
@Rule
public MockitoRule initRule = MockitoJUnit.rule();
....
}
2. MockitoAnnotations.initMocks
다음과 같이 테스트가 실행되기 전에 initMocks(this)
를 호출하는 방법도 있습니다.
public class MockitoTest {
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
}
3. MockitoJUnitRunner
다음과 같이 @RunWith
에 MockitoJUnitRunner로 설정하는 방법도 있습니다.
이 방법을 사용하면, 다른 JUnitRunner를 사용하지 못하기 때문에 위의 두가지 방법 중에 하나를 선택하시는 것이 좋습니다.
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {
...
}
위의 3가지 중에 하나를 테스트 코드에 적용하셨다면, 테스트 코드에서 @Mock
등의 Annotation을 사용할 수 있습니다.
@Mock
먼저 Annotation을 사용하지 않는 방법을 소개하고, 그 뒤에 Annotation을 사용하는 방법을 소개는 순서로 글을 작성하였습니다.
@Mock을 사용하지 않고 mock 객체 생성
아래 코드는 @Mock
을 사용하지 않고 mock 객체를 만드는 코드입니다.
@Test
public void mockingList_verify() {
List mockList = mock(ArrayList.class);
mockList.add("apple");
verify(mockList).add("apple");
}
@Mock으로 mock 객체 생성
다음과 같이 @Mock
을 사용하여 mock 객체를 만들 수 있습니다.
@Mock
을 사용하면 더 적은 코드로 mock 객체를 만들 수 있습니다.
@Mock
List mockList;
@Test
public void mockingList_verify() {
mockList.add("apple");
verify(mockList).add("apple");
}
다음은 when
으로 stubbing하는 예제입니다.
@Mock
List mockList;
@Test
public void mockingList_when() {
when(mockList.get(0)).thenReturn("apple");
when(mockList.get(1)).thenReturn("kiwi");
when(mockList.size()).thenReturn(10);
assertEquals("apple", mockList.get(0));
assertEquals("kiwi", mockList.get(1));
assertEquals(10, mockList.size());
}
@Spy
다음은 @Spy
없이 spy 객체를 만드는 방법입니다.
@Test
public void spyingList_verify() {
List spyList = spy(ArrayList.class);
spyList.add("apple");
verify(spyList).add("apple");
}
@Spy로 spy 객체 생성
다음과 같이 @Spy
를 사용하여 spy 객체를 만들 수 있습니다.
@Spy
List spyList;
@Test
public void spyingList_verify() {
spyList.add("apple");
verify(spyList).add("apple");
}
다음은 when
으로 stubbing하는 예제입니다.
@Spy
List spyList;
@Test
public void spyingList_when() {
doReturn("apple").when(spyList).get(0);
doReturn("kiwi").when(spyList).get(1);
doReturn(10).when(spyList).size();
assertEquals("apple", spyList.get(0));
assertEquals("kiwi", spyList.get(1));
assertEquals(10, spyList.size());
}
@Captor
다음은 @Captor
없이 ArgumentCaptor를 사용하는 코드입니다.
@Test
public void captorExample() {
ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
mockList.add("apple");
verify(mockList).add(arg.capture());
assertEquals("apple", arg.getValue());
}
@Captor로 ArgumentCaptor 객체 생성
다음과 같이 @Captor
를 사용하여 ArgumentCaptor 객체를 생성할 수 있습니다.
@Captor
ArgumentCaptor argCaptor;
@Test
public void captorAnnotationExample() {
mockList.add("apple");
verify(mockList).add(argCaptor.capture());
assertEquals("apple", argCaptor.getValue());
}
@InjectMocks
다음은 이번 예제에서 사용되는 두개의 클래스, Contacts와 ContactsDatabase입니다.
public class Contacts {
private ContactsDatabase db;
public Contacts() {
db = new ContactsDatabase();
}
public Contacts(ContactsDatabase db) {
this.db = db;
}
public String getUserInfo(int id) {
String userInfo= "{ " + db.getName(id) + ", "
+ db.getPhoneNumber(id) + " }";
return userInfo;
}
public void addUserInfo(String name, String number) {
db.addUserInfo(name, number);
}
}
public class ContactsDatabase {
List<String> names = new ArrayList<>();
List<String> numbers = new ArrayList<>();
public ContactsDatabase() {
names.add("AAA");
numbers.add("000-1234");
}
public String getName(int id) {
return names.get(id);
}
public String getPhoneNumber(int id) {
return numbers.get(id);
}
public void addUserInfo(String name, String number) {
names.add(name);
numbers.add(number);
}
}
Mock 객체 주입(Injection)
Contacts 클래스 안에는 ContactsDatabase 객체가 있습니다. Contacts 객체를 테스트하고 싶은데 ContactsDatabase와 의존성이 있어서 테스트가 쉽지 않습니다. 그래서 ContactsDatabase는 Mock으로 만들어 Contacts의 기능을 테스트하려고 합니다.
다음과 코드와 같이 먼저 ContactsDatabase는 Mock 객체로 만듭니다. 그리고 Contacts의 인자로 Mock 객체를 전달하면 Contacts는 실제 객체이지만 내부의 ContactsDatabase 객체는 Mock 객체가 됩니다.
Mock 객체에 when
으로 원하는 값이 리턴되도록 만들면, 특정 상황에서 Contacts이 어떻게 동작하는지 테스트할 수 있습니다.
@Mock
ContactsDatabase db;
@Test
public void injectingMock() {
Contacts contacts = new Contacts(db);
when(db.getName(0)).thenReturn("JS");
when(db.getPhoneNumber(0)).thenReturn("100-1234-5678");
assertEquals("{ JS, 100-1234-5678 }", contacts.getUserInfo(0));
}
@InjectMocks으로 Mock 객체 주입
위의 예제는 Mock 객체를 Contacts에 주입(Injection)하기 위해 Contacts(ContactsDatabase db)
라는 생성자를 만들었습니다.
@InjectMocks
을 사용하면 생성자를 만들지 않고도 Mock 객체를 주입할 수 있습니다.
먼저 다음과 같이 Contacts(ContactsDatabase db)
생성자를 삭제합니다.
public class Contacts {
private ContactsDatabase db;
public Contacts() {
db = new ContactsDatabase();
}
....
}
다음과 같이 Contacts를 @InjectMocks
와 함께 Member 변수로 정의하면, @Mock
으로 생성된 객체들이 Contacts에 주입(Injection)됩니다.
@Mock
ContactsDatabase db;
@InjectMocks
Contacts contacts = new Contacts();
@Test
public void injectingMock() {
when(db.getName(0)).thenReturn("JS");
when(db.getPhoneNumber(0)).thenReturn("100-1234-5678");
assertEquals("{ JS, 100-1234-5678 }", contacts.getUserInfo(0));
}
실행해보면, 위의 예제와 동일하게 동작하는 것을 알 수 있습니다.
Spy 객체에 Mock 객체 주입
Spy 객체에 Mock을 주입할 떄는 @InjectMocks
를 사용할 수 없습니다.
객체를 생성할 때 생성자에 Mock을 인자로 전달하는 방법을 사용해야 합니다.
다음과 같이 spy()
에 ContactsDatabase의 Mock 객체가 주입된 Contacts 객체를 인자로 전달하여 spy 객체를 만들 수 있습니다.
@Mock
ContactsDatabase db;
@Test
public void injectingMockIntoSpy() {
Contacts contacts = spy(new Contacts(db));
doReturn("JS").when(db).getName(0);
doReturn("100-1234-5678").when(db).getPhoneNumber(0);
assertEquals("{ JS, 100-1234-5678 }", contacts.getUserInfo(0));
}
정리
@Mock
, @Spy
, @Captor
, @InjectMocks
를 사용하는 방법에 대해서 알아보았습니다.
이 Annotation을 사용하면 더 적은 코드로 테스트 코드를 작성할 수 있습니다.
Related Posts
- Java - Unsupported class file major version 61 에러
- Java - String.matches()로 문자열 패턴 확인 및 다양한 예제 소개
- Java - 문자열 공백제거 (trim, replace)
- Java - replace()와 replaceAll()의 차이점
- Java - ArrayList 초기화, 4가지 방법
- Java - 배열 정렬(Sorting) (오름차순, 내림차순)
- Java - 문자열(String)을 비교하는 방법 (==, equals, compare)
- Java - StringBuilder 사용 방법, 예제
- Java - 로그 출력, 파일 저장 방법 (Logger 라이브러리)
- Java IllegalArgumentException 의미, 발생 이유
- Java - NullPointerException 원인, 해결 방법
- Seleninum의 ConnectionFailedException: Unable to establish websocket connection 해결
- Java - compareTo(), 객체 크기 비교
- Java - BufferedWriter로 파일 쓰기
- Java - BufferedReader로 파일 읽기
- Java charAt() 함수 알아보기
- Java - BigInteger 범위, 비교, 연산, 형변환
- Java contains()로 문자(대소문자 X) 포함 확인
- Java - Set(HashSet)를 배열로 변환
- Java - 문자열 첫번째 문자, 마지막 문자 확인
- Java - 문자열 한글자씩 자르기
- Java - 문자열 단어 개수 가져오기
- Java - 1초마다 반복 실행
- Java - 배열을 Set(HashSet)로 변환
- Java - 여러 Set(HashSet) 합치기
- Java - 명령행 인자 입력 받기
- Java - 리스트 역순으로 순회, 3가지 방법
- Java - 특정 조건으로 리스트 필터링, 3가지 방법
- Java - HashMap 모든 요소들의 합계, 평균 계산
- Java - 특정 조건으로 HashMap 필터링
- Java - 싱글톤(Singleton) 패턴 구현
- Java - 숫자 왼쪽에 0으로 채우기
- Java - String 배열 초기화 방법
- Java - 정렬된 순서로 Map(HashMap) 순회
- Java - HashMap에서 key, value 가져오기