Mockito 3 Answere RETURNS DEEP STUBS

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()
}

 

follow us on