티스토리 뷰
Mokito Framework
Mockito는 Java에서 Mock 객체를 만들고 테스트하는 데 사용되는 인기있는 프레임워크
다른 클래스에 대한 가짜(Mock) 객체를 만들고, 해당 객체가 어떻게 동작해야 하는지를 정의하고,
테스트 중에 해당 객체의 동작을 검증
Mockito는 테스트 주도 개발(Test Driven Development, TDD) 및 단위 테스트 작성을 보다 쉽고 효율적으로 만들어줍니다.
JUnit과 같은 테스트 프레임워크와 함께 사용
- Mock 객체 생성: Mockito를 사용하여 다른 클래스의 가짜(Mock) 객체를 생성할 수 있습니다.
- 동작 정의: Mockito를 사용하여 Mock 객체의 특정 메서드 호출에 대한 동작을 정의할 수 있습니다. 예를 들어, 특정 메서드가 호출될 때 반환해야 하는 값을 지정하거나, 예외를 던져야 할 때 그러한 동작을 설정할 수 있습니다.
- 메서드 호출 검증: Mockito를 사용하여 Mock 객체의 메서드 호출이 특정 조건에 따라 예상대로 이루어졌는지를 검증할 수 있습니다. 예를 들어, 메서드가 정확히 한 번 호출되었는지, 특정 매개변수와 함께 호출되었는지 등을 검증할 수 있습니다.
- Spy 객체: Mockito를 사용하여 실제 객체의 일부를 Mock 객체로 변환할 수 있습니다. 이것을 통해 일부 메서드의 동작을 변경하거나, 메서드 호출을 감시하고 싶을 때 유용합니다.
- 다양한 기능 제공: Mockito는 많은 다른 기능도 제공합니다. 예를 들어, 인자 일치자(argument matchers)를 사용하여 메서드 호출에 대한 인자를 유연하게 처리할 수 있으며, Stubbing, Verification 등의 기능을 통해 테스트 코드를 작성하고 실행할 수 있습니다.
@Mock
- 모의 객체(Mock)를 생성할 때 사용
- Mockito는 해당 필드에 대한 모의 객체를 생성하고 주입
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import static org.mockito.Mockito.when;
public class MyServiceTest {
@Mock
private MyRepository myRepository; //모의 객체
@Test
public void testSomeMethod() {
when(myRepository.getData()).thenReturn("Mocked Data"); //모의 객체의 동작을 설정
// 테스트 코드 작성
}
}
Mock 객체 주입하기
- Mockito 3.4.0 버전 이후부터는 MockitoAnnotations.initMocks(this) 대신에 MockitoAnnotations.openMocks(this) 메서드를 사용
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class MyServiceTest {
private MyService myService;
@Mock
private MyRepository myRepository;
@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
myService = new MyService(myRepository); // 모의 객체를 주입
}
@Test
public void testSomeMethod() {
// 테스트 코드 작성
}
}
@InjectMocks 로 Mock 객체 주입하기
- JUnit 5부터는 @RunWith 어노테이션 대신에 @ExtendWith 어노테이션을 사용
- @ExtendWith(MockitoExtension.class) 을 사용하여 테스트 클래스에 Mockito 기능을 확장
- Mockito 라이브러리에서 제공하는 MockitoExtension을 사용하여 Mockito 관련 기능을 확장
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class MyServiceTest {
@InjectMocks
private MyService myService; // 모의 객체가 주입될 대상
@Mock
private MyRepository myRepository; // 모의 객체 생성
@Test
public void testSomeMethod() {
// 테스트 코드 작성
}
}
Mockito.when
Mock 객체의 메서드 호출에 대한 동작을 정의하는 데 사용
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
public class CalculatorTest {
@Mock
private Calculator calculatorMock; // Calculator 클래스의 목 객체 생성
@Test
public void testAddition() {
// add() 메서드가 호출될 때 2와 3을 전달 받으면 5를 리턴하도록 설정
when(calculatorMock.add(2, 3)).thenReturn(5);
// 목 객체를 사용하여 테스트
int result = calculatorMock.add(2, 3);
// 결과 확인
assertEquals(5, result);
}
}
thenReturn(): 메서드 호출이 발생했을 때 목 객체가 반환할 값을 지정하는 데 사용
thenThrow(): 메서드 호출이 발생했을 때 목 객체가 예외를 던지도록 설정하는 데 사용
thenAnswer(): 메서드 호출이 발생했을 때 사용자가 정의한 동적 로직에 따라 동작하도록 설정하는 데 사용
import static org.mockito.Mockito.*;
public class ExampleTest {
@Mock
private Calculator calculatorMock; // Calculator 클래스의 목 객체 생성
@Test
public void testWhenThenReturn() {
// add 메서드 호출시 2와 3이 들어올 경우 5를 리턴하도록 설정
when(mockExample.add(2, 3)).thenReturn(5);
// 테스트
assertEquals(5, mockExample.add(2, 3));
}
@Test(expected = IllegalArgumentException.class)
public void testWhenThenThrow() {
// divide 메서드 호출시 0이 들어올 경우 IllegalArgumentException을 던지도록 설정
when(mockExample.divide(anyInt(), eq(0))).thenThrow(new IllegalArgumentException());
// 테스트
mockExample.divide(10, 0);
}
@Test
public void testThenAnswer() {
// when - thenAnswer 사용하여 동적으로 값을 계산
when(mockExample.calculate(anyInt())).thenAnswer(invocation -> {
int argument = invocation.getArgument(0);
return argument * 2;
});
// 테스트
assertEquals(10, mockExample.calculate(5));
}
}
Argument Matchers
목 객체의 메서드 호출에 대한 인자를 유연하게 처리
특정한 값을 정확하게 지정하지 않고도 메서드 호출을 검증하거나 설정
- any(Class<T> type): 특정 클래스 타입의 어떤 인자든 일치합니다.
- anyInt(), anyLong(), anyDouble(), 등: int, long, double 등의 기본 데이터 타입에 대한 어떤 값이든 일치합니다.
- eq(T value): 지정된 값과 동일한 인자와 일치합니다.
- isNull(): null 값과 일치합니다.
- notNull(): null이 아닌 값과 일치합니다.
- anyString(), anyListOf(Class<T> clazz), 등: String, List 등 특정 타입의 객체와 일치합니다.
- argThat(ArgumentMatcher<T> matcher): 사용자가 정의한 조건에 따라 일치하는 인자와 일치합니다.
https://javadoc.io/static/org.mockito/mockito-core/5.11.0/org/mockito/ArgumentMatchers.html
//stubbing using built-in anyInt() argument matcher
when(mockedList.get(anyInt())).thenReturn("element");
//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
when(mockedList.contains(argThat(isValid()))).thenReturn(true);
//following prints "element"
System.out.println(mockedList.get(999));
//you can also verify using an argument matcher
verify(mockedList).get(anyInt());
//argument matchers can also be written as Java 8 Lambdas
verify(mockedList).add(argThat(someString -> someString.length() > 5));
any(), anyInt() matchers는 타입 체크를 하기 때문에 null 인자는 isNull()을 사용해야 한다.
// stubbing using anyBoolean() argument matcher
when(mock.dryRun(anyBoolean())).thenReturn("state");
// below the stub won't match, and won't return "state"
mock.dryRun(null);
// either change the stub
when(mock.dryRun(isNull())).thenReturn("state");
mock.dryRun(null); // ok
// or fix the code ;)
when(mock.dryRun(anyBoolean())).thenReturn("state");
mock.dryRun(true); // ok
argument matchers 사용한다면 메서드의 모든 인자는 matchers를 사용해야 한다.
verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//above is correct - eq() is also an argument matcher
verify(mock).someMethod(anyInt(), anyString(), "third argument");
//above is incorrect - exception will be thrown because third argument is given without argument matcher.
Additional matchers
argument matchers를 좀 더 복잡한 일치 조건을 정의하거나, 특정한 상황에 매칭을 수행할 때 유용
https://javadoc.io/static/org.mockito/mockito-core/5.11.0/org/mockito/AdditionalMatchers.html
//anything but not "ejb"
mock.someMethod(not(eq("ejb")));
//not "ejb" and not "michael jackson"
mock.someMethod(and(not(eq("ejb")), not(eq("michael jackson"))));
//1 or 10
mock.someMethod(or(eq(1), eq(10)));
Mockito.verify 호출 횟수
목 객체의 메서드 호출을 검증하는 데 사용
목 객체의 특정 메서드가 지정된 횟수와 매개변수로 호출되었는지를 확인하는 데 유용
기본값은 times(1)로 한 번 호출되었는지를 검증
//using mock
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
//following two verifications work exactly the same - times(1) is used by default
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
//exact number of invocations verification
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");
//verification using never(). never() is an alias to times(0)
verify(mockedList, never()).add("never happened");
//verification using atLeast()/atMost()
verify(mockedList, atMostOnce()).add("once");
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("three times");
verify(mockedList, atMost(5)).add("three times");
Mockito.verify 호출 순서 InOrder
여러 목 객체가 순서대로 호출되었는지를 검증할 때 사용
https://javadoc.io/static/org.mockito/mockito-core/5.11.0/org/mockito/InOrder.html
//Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);
//using mocks
firstMock.add("was called first");
secondMock.add("was called second");
//create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = inOrder(firstMock, secondMock);
//following will make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
연속적으로 Stubbing 하기
when(mock.someMethod("some arg"))
.thenThrow(new RuntimeException())
.thenReturn("foo");
//First call: throws runtime exception:
mock.someMethod("some arg");
//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));
//Any consecutive call: prints "foo" as well (last stubbing wins).
System.out.println(mock.someMethod("some arg"));
when(mock.someMethod("some arg"))
.thenReturn("one", "two", "three");
실행되지 않았는지 확인하기 never()
//using mocks - only mockOne is interacted
mockOne.add("one");
//ordinary verification
verify(mockOne).add("one");
//verify that method was never called on a mock
verify(mockOne, never()).add("two");
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
'Java' 카테고리의 다른 글
[Java] List, Map 정적 팩토리 메소드 (0) | 2023.09.10 |
---|---|
[Java] JSON 변환 JSON-Java org.json (0) | 2023.08.28 |
[Java] JSON 변환 net.sf.json-lib (0) | 2023.08.18 |
[Java] Stream 병렬 데이터 처리와 성능 (0) | 2023.08.15 |
[Java] 스트림으로 데이터 수집 - 분할 (0) | 2023.08.09 |