Mockito Answers 관련 javadoc 번역 및 테스트를 해보았다.
Unstubbed mock의 동작 방식을 구현해 주고 싶을 때 사용한다.
@Mock(answer = Answers.RETURNS_MOCKS)
이런 식으로 사용할 수 있다.
어노테이션의 필드가 아니라면 모킹할 때 직접적으로 mock(class, Answer)
의 형태로 사용할 수 있다.
Foo mock = mock(Foo.class, RETURNS_DEEP_STUBS);
Answers enum에 있는 값들을 살펴보자.
@Mock에 아무 설정을 주지 않으면 RETURNS_DEFAULTS로 설정된다.
Mock 객체가 stubbing 되지 않았으면, 빈 값을 반환한다.
(GloballyConfiguredAnswer 사용)
public class YenyService {
public String saveYeny(String yeny) {
String yenyString = "i love " + yeny + " yeny";
return yenyString;
}
}
@ExtendWith(MockitoExtension.class)
public class YenyTest {
@Mock(answer = RETURNS_DEFAULTS)
private YenyService yenyService;
@Test
void saveyenyWithStaticMockTest() {
YenyService yenyService = mock(YenyService.class, RETURNS_DEFAULTS);
String yenyName = yenyService.saveYeny("Macintosh");
System.out.println("saveyenyWithStaticMockTest yenyName = " + yenyName);
}
@Test
void saveyenyWithAnnotationMockTest() {
String yenyName = yenyService.saveYeny("Macintosh");
System.out.println("saveyenyWithAnnotationMockTest yenyName = " + yenyName);
}
}
stubbing 되지 않은 메소드를 호출할 때, 보통은 NullPointerException을 발생시킨다.
RETURNS_SMART_NULLS은 null 대신 SmartNull을 반환한다.
SmartNull은 SmartNullPointerException라는 NPE보다 나은 예외를 발생시킨다. stack trace에서 unstubbed method의 라인을 찾는 데에 더 유용하다.
RETURNS_DEFAULTS 예제의 YenyService 클래스 사용
@ExtendWith(MockitoExtension.class)
public class YenyTest {
@Mock(answer = RETURNS_SMART_NULLS)
private YenyService yenyService;
@Test
void saveyenyWithStaticMockTest() {
YenyService yenyService = mock(YenyService.class, RETURNS_SMART_NULLS);
String yenyName = yenyService.saveYeny("Macintosh");
System.out.println("saveYenyWithStaticMockTest yenyName = " + yenyName);
System.out.println("saveYenyWithStaticMockTest yenyName = " + yenyName.toUpperCase());
}
@Test
void saveyenyWithAnnotationMockTest() {
String yenyName = yenyService.saveYeny("Macintosh");
System.out.println("saveYenyWithAnnotationMockTest yenyName = " + yenyName);
System.out.println("saveYenyWithStaticMockTest yenyName = " + yenyName.toUpperCase());
}
}
오잉 NPE가 아니라 SmartNullPointerException이 발생할 줄 알았는데, 그냥 아무것도 안뜬다.
Unstubbed method에 대해 일반적으로 null을 반환하지만, 모키토가 응답을 감지하고 그에 맞는 응답을 만들도록 하려면 RETURNS_MOCKS를 사용하면 된다.
public class YenyService {
private Yeny yeny;
public Yeny getYeny() {
return yeny;
}
public class Yeny {
}
}
@ExtendWith(MockitoExtension.class)
class YenyTest {
@Mock private YenyService yenyService;
@Test
void getYenyWithStaticMockTest() {
YenyService yenyService = mock(YenyService.class);
Yeny yeny = yenyService.getYeny();
Yeny yeny1 = yenyService.getYeny();
System.out.println("yeny = " + yeny);
System.out.println("yeny1 = " + yeny1);
assertEquals(yeny, yeny1);
}
@Test
void getYenyWithAnnotationMockTest() {
Yeny yeny = yenyService.getYeny();
Yeny yeny1 = yenyService.getYeny();
System.out.println("yeny = " + yeny);
System.out.println("yeny1 = " + yeny1);
assertEquals(yeny, yeny1);
}
}
null이 나온다. 또한, yeny, yeny1 모두 null이므로 assertEquals를 통과한다.
@ExtendWith(MockitoExtension.class)
class YenyTest {
@Mock(answer = Answers.RETURNS_MOCKS)
private YenyService yenyService;
@Test
void getYenyWithStaticMockTest() {
YenyService yenyService = mock(YenyService.class, RETURNS_MOCKS);
Yeny yeny = yenyService.getYeny();
Yeny yeny1 = yenyService.getYeny();
System.out.println("yeny = " + yeny);
System.out.println("yeny1 = " + yeny1);
assertNotEquals(yeny, yeny1);
}
@Test
void getYenyWithAnnotationMockTest() {
Yeny yeny = yenyService.getYeny();
Yeny yeny1 = yenyService.getYeny();
System.out.println("yeny = " + yeny);
System.out.println("yeny1 = " + yeny1);
assertNotEquals(yeny, yeny1);
}
}
모키토가 unstubbed method의 응답을 감지하여 새로운 응답을 생성했기 때문에, 모두 다른 객체가 나온다.
public class YenyService {
private Yeny yeny;
public Yeny getYeny() {
return yeny;
}
public class Yeny {
private Best best;
public Best getBest() {
return best;
}
public class Best {
}
}
}
@ExtendWith(MockitoExtension.class)
class YenyTest {
@Mock(answer = Answers.RETURNS_MOCKS)
private YenyService yenyService;
@Test
void getYenyWithStaticMockTest() {
YenyService yenyService = mock(YenyService.class, RETURNS_MOCKS);
Yeny yeny = yenyService.getYeny();
Yeny yeny1 = yenyService.getYeny();
System.out.println("yeny = " + yeny);
System.out.println("yeny1 = " + yeny1);
assertNotEquals(yeny, yeny1);
System.out.println("yeny.getBest() = " + yeny.getBest());
System.out.println("yeny1.getBest() = " + yeny1.getBest());
assertNotEquals(yeny.getBest(), yeny1.getBest());
}
@Test
void getYenyWithAnnotationMockTest() {
Yeny yeny = yenyService.getYeny();
Yeny yeny1 = yenyService.getYeny();
System.out.println("yeny = " + yeny);
System.out.println("yeny1 = " + yeny1);
assertNotEquals(yeny, yeny1);
System.out.println("yeny.getBest() = " + yeny.getBest());
System.out.println("yeny1.getBest() = " + yeny1.getBest());
assertNotEquals(yeny.getBest(), yeny1.getBest());
}
}
중첩된 클래스들이 있는 경우, 보통의 경우 아래와 같이 중간 단계에 속한 모든 mock method가 필요하다.
public class YenyService {
private Yeny yeny;
public Yeny getYeny() {
return yeny;
}
public class Yeny {
private Best best;
public Best getBest() {
return best;
}
public class Best {
private String yenyName;
public String getYenyName() {
return yenyName;
}
}
}
}
@ExtendWith(MockitoExtension.class)
class YenyTest {
@Mock private YenyService yenyService;
@Mock private Yeny yeny;
@Mock private Best best;
@Test
void withStaticMockTest() {
YenyService yenyService = mock(YenyService.class);
Yeny yeny = mock(Yeny.class);
Best best = mock(Best.class);
when(yenyService.getYeny()).thenReturn(yeny);
when(yeny.getBest()).thenReturn(best);
when(best.getYenyName()).thenReturn("i love yeny");
when(yenyService.getYeny().getBest().getYenyName()).thenReturn("i love yeny");
assertEquals("i love yeny", yenyService.getYeny().getBest().getYenyName());
}
@Test
void withAnnotationMockTest() {
when(yenyService.getYeny()).thenReturn(yeny);
when(yeny.getBest()).thenReturn(best);
when(best.getYenyName()).thenReturn("i love yeny");
when(yenyService.getYeny().getBest().getYenyName()).thenReturn("i love yeny");
assertEquals("i love yeny", yenyService.getYeny().getBest().getYenyName());
}
}
YenyService 코드는 위와 같음
@ExtendWith(MockitoExtension.class)
class YenyTest {
@Mock(answer = RETURNS_DEEP_STUBS)
private YenyService yenyService;
@Test
void withStaticMockTest() {
YenyService yenyService = mock(YenyService.class, RETURNS_DEEP_STUBS);
when(yenyService.getYeny().getBest().getYenyName()).thenReturn("i love yeny");
assertEquals("i love yeny", yenyService.getYeny().getBest().getYenyName());
}
@Test
void withAnnotationMockTest() {
when(yenyService.getYeny().getBest().getYenyName()).thenReturn("i love yeny");
assertEquals("i love yeny", yenyService.getYeny().getBest().getYenyName());
}
}
RETURNS_DEEP_STUB를 사용하면, 내부적으로 중간 단계의 객체들을 모킹해준다.
우리는 가장 마지막 결과만 모킹해주면 된다.
마지막 결과가 primitive 타입이나 final 클래스인 경우에는 사용할 수 없다!
Unstubbed method에 대해 실제 메소드를 호출하는 partial mock 객체를 생성한다.
CALLS_REAL_METHODS이 사용되지 않았다면, unstubbed method는 Nice Mock을 사용하고 null을 반환한다.
CALLS_REAL_METHODS는 unstubbed method에 대해서만 동작한다. Stubbed method라면 스텁된 결과가 반환된다.
public class YenyService {
public String saveYeny(String yeny) {
String yenyString = "i love " + yeny + " yeny";
System.out.println("yenyString = " + yenyString);
return yenyString;
}
}
@ExtendWith(MockitoExtension.class)
class YenyTest {
@Mock(answer = CALLS_REAL_METHODS)
private YenyService yenyService;
@Test
void withStaticMockTest() {
YenyService yenyService = mock(YenyService.class, Mockito.CALLS_REAL_METHODS);
String yeny = yenyService.saveYeny("Yeny");
verify(yenyService).saveYeny("Yeny");
assertEquals("i love Yeny yeny", yeny);
}
@Test
void withAnnotationMockTest() {
String yeny = yenyService.saveYeny("Yeny");
verify(yenyService).saveYeny("Yeny");
assertEquals("i love Yeny yeny", yeny);
}
}
Builder 클래스는 build() 메소드 외에는 자기 자신 self 인스턴스를 리턴한다.
자기 자신객체를 리턴하는 모든 메소드에 대해 mocking 을 편하게 지정하기 위해 사용한다.