A complex application consists of multiple classes used in one another, we might need to mock method which returns another class which in turn again returns nested class and so on. Mockito helps us to mock return value 1 way is to mock all the intermittent responses till the desired one.
AppleService -> Apple -> Macintosh
to mock the Macintosh
class method, we need to mock Apple
and AppleService
first.
package com.example.mokito3.sujan;
public class AppleService {
private Apple apple;
public Apple getApple() {
return apple;
}
public class Apple {
private Macintosh macintosh;
public Macintosh getMacintosh() {
return macintosh;
}
}
public class Macintosh {
private String appleName;
public String getAppleName() {
return appleName;
}
}
}
package com.example.mokito3.sujan;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
@Mock
private AppleService appleService;
@Mock
private AppleService.Apple apple;
@Mock
private AppleService.Macintosh macintosh;
@Test
void saveAppleWithStaticMockTest() {
AppleService appleService = mock(AppleService.class);
AppleService.Apple apple = mock(AppleService.Apple.class);
AppleService.Macintosh macintosh = mock(AppleService.Macintosh.class);
when(appleService.getApple()).thenReturn(apple);
when(apple.getMacintosh()).thenReturn(macintosh);
when(macintosh.getAppleName()).thenReturn("i love apple");
when(appleService.getApple().getMacintosh().getAppleName()).thenReturn("i love apple");
assertEquals("i love apple", appleService.getApple().getMacintosh().getAppleName());
}
@Test
void saveAppleWithAnnotationMockTest() {
when(appleService.getApple()).thenReturn(apple);
when(apple.getMacintosh()).thenReturn(macintosh);
when(macintosh.getAppleName()).thenReturn("i love apple");
when(appleService.getApple().getMacintosh().getAppleName()).thenReturn("i love apple");
assertEquals("i love apple", appleService.getApple().getMacintosh().getAppleName());
}
}
plugins {
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories { jcenter() }
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
useJUnitPlatform()
}
How RETURNS DEEP STUBS works internally.
RETURNS_DEEP_STUBS
internally mocks all the intermediate instances in invocation order.
below stub
AppleService appleService = mock(AppleService.class, RETURNS_DEEP_STUBS);
when(appleService.getApple("Macintosh").getMacintosh().getAppleName()).thenReturn("i love apple");
will internally be converted into
AppleService appleService = mock(AppleService.class);
Macintosh macintosh = mock(Macintosh.class);
when(appleService.getApple("Macintosh")).thenReturn(macintosh);
when(macintosh.getAppleName()).thenReturn("i love apple");
with the help of RETURNS_DEEP_STUBS
we don't need to mock intermittent classes. Mockito will do that, we just need to mock the desired end result only.
WARNING: This feature should rarely be required for regular clean code!
Mocking a mock should be used with complex and legacy code only. Mocking a mock to return a mock, to return a mock to return the desired result is the violation of Law of Demeter and anti-pattern as well.
every time a mock returns a mock a fairy dies.
package com.example.mokito3.sujan;
public class AppleService {
private Apple apple;
public Apple getApple() {
return apple;
}
public class Apple {
private Macintosh macintosh;
public Macintosh getMacintosh() {
return macintosh;
}
}
public class Macintosh {
private String appleName;
public String getAppleName() {
return appleName;
}
}
}
package com.example.mokito3.sujan;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
@Mock(answer = RETURNS_DEEP_STUBS)
private AppleService appleService;
@Test
void saveAppleWithStaticMockTest() {
AppleService appleService = mock(AppleService.class, RETURNS_DEEP_STUBS);
when(appleService.getApple().getMacintosh().getAppleName()).thenReturn("i love apple");
assertEquals("i love apple", appleService.getApple().getMacintosh().getAppleName());
}
@Test
void saveAppleWithAnnotationMockTest() {
when(appleService.getApple().getMacintosh().getAppleName()).thenReturn("i love apple");
assertEquals("i love apple", appleService.getApple().getMacintosh().getAppleName());
}
}
plugins {
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories { jcenter() }
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
useJUnitPlatform()
}
RETURNS_DEEP_STUBS
can be used with Verification as well but with the last mock in the chain only.
package com.example.mokito3.sujan;
public class AppleService {
private Apple apple;
public Apple getApple(String appleName) {
System.out.println("appleName = " + appleName);
return apple;
}
public class Apple {
private Macintosh macintosh;
public Macintosh getMacintosh() {
return macintosh;
}
}
public class Macintosh {
private String appleName;
public String getAppleName() {
return appleName;
}
}
}
package com.example.mokito3.sujan;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
@Mock(answer = RETURNS_DEEP_STUBS)
private AppleService appleService;
@Test
void saveAppleWithStaticMockTest() {
AppleService appleService = mock(AppleService.class, RETURNS_DEEP_STUBS);
when(appleService.getApple("Macintosh").getMacintosh().getAppleName()).thenReturn("i love apple");
when(appleService.getApple("Fuji").getMacintosh().getAppleName()).thenReturn("i love apple");
appleService.getApple("Macintosh").getMacintosh().getAppleName();
appleService.getApple("Fuji").getMacintosh().getAppleName();
InOrder inOrder = inOrder(appleService.getApple("Macintosh").getMacintosh(),
appleService.getApple("Fuji").getMacintosh());
inOrder.verify(appleService.getApple("Macintosh").getMacintosh(), times(1)).getAppleName();
inOrder.verify(appleService.getApple("Fuji").getMacintosh(), atLeast(1)).getAppleName();
}
@Test
void saveAppleWithAnnotationMockTest() {
when(appleService.getApple("Macintosh").getMacintosh().getAppleName()).thenReturn("i love apple");
when(appleService.getApple("Fuji").getMacintosh().getAppleName()).thenReturn("i love apple");
appleService.getApple("Macintosh").getMacintosh().getAppleName();
appleService.getApple("Fuji").getMacintosh().getAppleName();
InOrder inOrder = inOrder(appleService.getApple("Macintosh").getMacintosh(),
appleService.getApple("Fuji").getMacintosh());
inOrder.verify(appleService.getApple("Macintosh").getMacintosh(), times(1)).getAppleName();
inOrder.verify(appleService.getApple("Fuji").getMacintosh(), atLeast(1)).getAppleName();
}
}
plugins {
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories { jcenter() }
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
useJUnitPlatform()
}
RETURNS_DEEP_STUBS
feature works with most of the cases but has few limitations as well. it will not work with methods whose return type cannot be mocked, for example, methods with primitive return type for is a final class.
package com.example.mokito3.sujan;
public class AppleService {
private Apple apple;
public Apple getApple(String appleName) {
System.out.println("appleName = " + appleName);
return apple;
}
public final class Apple {
private Macintosh macintosh;
public Macintosh getMacintosh() {
return macintosh;
}
}
public class Macintosh {
private String appleName;
public String getAppleName() {
return appleName;
}
}
}
j
package com.example.mokito3.sujan;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
@Mock(answer = RETURNS_DEEP_STUBS)
private AppleService appleService;
@Test
void saveAppleWithStaticMockTest() {
AppleService appleService = mock(AppleService.class, RETURNS_DEEP_STUBS);
when(appleService.getApple("Macintosh").getMacintosh().getAppleName()).thenReturn("i love apple");
when(appleService.getApple("Fuji").getMacintosh().getAppleName()).thenReturn("i love apple");
appleService.getApple("Macintosh").getMacintosh().getAppleName();
appleService.getApple("Fuji").getMacintosh().getAppleName();
InOrder inOrder = inOrder(appleService.getApple("Macintosh").getMacintosh(),
appleService.getApple("Fuji").getMacintosh());
inOrder.verify(appleService.getApple("Macintosh").getMacintosh(), times(1)).getAppleName();
inOrder.verify(appleService.getApple("Fuji").getMacintosh(), atLeast(1)).getAppleName();
}
@Test
void saveAppleWithAnnotationMockTest() {
when(appleService.getApple("Macintosh").getMacintosh().getAppleName()).thenReturn("i love apple");
when(appleService.getApple("Fuji").getMacintosh().getAppleName()).thenReturn("i love apple");
appleService.getApple("Macintosh").getMacintosh().getAppleName();
appleService.getApple("Fuji").getMacintosh().getAppleName();
InOrder inOrder = inOrder(appleService.getApple("Macintosh").getMacintosh(),
appleService.getApple("Fuji").getMacintosh());
inOrder.verify(appleService.getApple("Macintosh").getMacintosh(), times(1)).getAppleName();
inOrder.verify(appleService.getApple("Fuji").getMacintosh(), atLeast(1)).getAppleName();
}
}
plugins {
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories { jcenter() }
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
useJUnitPlatform()
}