Mockito Stubbing With Custom Answers

0

For simple stubbing, with just need to use thenReturn() or thenThrow() in case of exception. If your stubbed method needs to return result based on some computation then you can use the Answer callback. In this example, I will show you Mockito Stubbing with Custom Answers.
Answers allows stubbing with the generic Answer interface.

public interface Answer<T> {
    T answer(InvocationOnMock invocation) throws Throwable;
}

What do we understand from the above interface?
We can stub the mock object’s method in such a way that when the stubbed method is invoked, the answer(invocation) method of the Answer. The Answer object’s answer() method will return the dynamic result.

Mockito is an open source mock unit testing framework for Java. In this article, we will look into some of the stubbing examples using answer callback.

Below are my setup details:

System Under Test

All our stubbing examples follow a simple model. Our model consists of interface Tree and class Seed. We know when we plant a seed, it eventually grows into a tree. We have included this in Tree interface.

Tree:

package com.javarticles.mockito;

public interface Tree {
    Tree grow(Seed seed);
    
    String getName();
    
    void pluckFruits();
}

AppleTree:

package com.javarticles.mockito;

public interface AppleTree extends Tree {
}

Seed:

package com.javarticles.mockito;

public class Seed {
    private String name;
    private boolean isRotten;
    private Tree tree;
    
    public Seed(String name) {
        this.name = name;
    }
    
    public Tree grow() {
        if (!isRotten() && tree != null) {
            return tree.grow(this);
        }  
        return null;
    }
      
    public boolean equals(Object o) {
        if (o == null) {
            return false;
        }
        if (!(o instanceof Seed)) {
            return false;
        }
        Seed seed = (Seed) o;
        return name.equals(seed.getName());
    }

    public String getName() {
        return name;
    }
    
    public String toString() {
        return "Seed(" + name + ")";
    }

    public boolean isRotten() {
        return isRotten;
    }

    public void setRotten(boolean isRotten) {
        this.isRotten = isRotten;
    }

    public void setTree(Tree tree) {
        this.tree = tree;
    }        
    
}

AppleSeed:

package com.javarticles.mockito;

public class AppleSeed extends Seed {

    public AppleSeed() {
        super("Apple");
    }

}

Stubbing with Answer Callback

In stubbingWithAnswer(), we we return different answers based on whether the seed is apple seed or seed of some other tree. If it is apple seed we train tree.grows() to return apple tree else a generic tree. We call thenAnswer to return a custom answer. The custom answer is an anonymous instance of Answer. Based on the invocation parameter we, return apple tree or tree.

when(tree.grow(isA(Seed.class))).thenAnswer(new Answer() {

            public Tree answer(InvocationOnMock invocation) throws Throwable {
                Seed seed = invocation.getArgumentAt(0, Seed.class);
                return seed.getName().equals("Apple") ? appleTree : tree;
            }
        });

StubbingWithAnswersTests:

package com.javarticles.mockito;

import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.*;
import static org.testng.Assert.assertEquals;

import org.mockito.exceptions.misusing.CannotVerifyStubOnlyMock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class StubbingWithAnswersTests {
    private Tree tree;
    private Seed seed = new Seed("Apple");
    private AppleTree appleTree;
    private AppleSeed appleSeed = new AppleSeed();
    
    @BeforeMethod
    public void createMock() {
        tree = mock(Tree.class);
        seed.setTree(tree);
        appleTree = mock(AppleTree.class);
        appleSeed.setTree(appleTree);
        when(appleTree.getName()).thenReturn("Apple Tree");
    }
    
    @Test
    public void stubbingWithAnswer() {
        when(tree.grow(isA(Seed.class))).thenAnswer(new Answer() {

            public Tree answer(InvocationOnMock invocation) throws Throwable {
                Seed seed = invocation.getArgumentAt(0, Seed.class);
                return seed.getName().equals("Apple") ? appleTree : tree;
            }
        });
        
        assertEquals(tree.grow(seed), appleTree);        
    }
}

Stubbing with Answer Alias

In this example, we will use method then(answer) which is an alias of thenAnswer(answer) of {@link #thenAnswer(Answer)}. This alias allows
more readable tests on some occasions.

package com.javarticles.mockito;

import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.*;
import static org.testng.Assert.assertEquals;

import org.mockito.exceptions.misusing.CannotVerifyStubOnl
yMock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class StubbingWithAnswersTests {
    private Tree tree;
    private Seed seed = new Seed("Apple");
    private AppleTree appleTree;
    private AppleSeed appleSeed = new AppleSeed();
    
    @BeforeMethod
    public void createMock() {
        tree = mock(Tree.class);
        seed.setTree(tree);
        appleTree = mock(AppleTree.class);
        appleSeed.setTree(appleTree);
        when(appleTree.getName()).thenReturn("Apple Tree");
    }
    
    @Test
    public void stubbingWithAnswer() {
        when(tree.grow(isA(Seed.class))).thenAnswer(new Answer() {

            public Tree answer(InvocationOnMock invocation) throws Throwable {
                Seed seed = invocation.getArgumentAt(0, Seed.class);
                return seed.getName().equals("Apple") ? appleTree : tree;
            }
        });
        
        assertEquals(tree.grow(seed), appleTree);        
    }
 
    @Test
    public void stubbingWithAnswerAlias() {
        when(tree.grow(isA(Seed.class))).then(new ReturnTree());
        
        assertEquals(tree.grow(seed), appleTree);        
    }
    
    private class ReturnTree implements Answer {

        public Tree answer(InvocationOnMock invocation) throws Throwable {
            Seed seed = invocation.getArgumentAt(0, Seed.class);
            return seed.getName().equals("Apple") ? appleTree : tree;
        }
        
    }
}

Stubbing With Answer Callbacks Consecutively

In code>stubbingWithConsecuitiveAnswers(), we stub the mock object with multiple answer callbacks. Each method call, returns a different apple object.

package com.javarticles.mockito;

import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.*;
import static org.testng.Assert.assertEquals;

import org.mockito.exceptions.misusing.CannotVerifyStubOnlyMock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class StubbingWithAnswersTests {
    private Tree tree;
    private Seed seed = new Seed("Apple");
    private AppleTree appleTree;
    private AppleSeed appleSeed = new AppleSeed();
    
    @BeforeMethod
    public void createMock() {
        tree = mock(Tree.class);
        seed.setTree(tree);
        appleTree = mock(AppleTree.class);
        appleSeed.setTree(appleTree);
        when(appleTree.getName()).thenReturn("Apple Tree");
    }
    
    @Test
    public void stubbingWithAnswer() {
        when(tree.grow(isA(Seed.class))).thenAnswer(new Answer() {

            public Tree answer(InvocationOnMock invocation) throws Throwable {
                Seed seed = invocation.getArgumentAt(0, Seed.class);
                return seed.getName().equals("Apple") ? appleTree : tree;
            }
        });
        
        assertEquals(tree.grow(seed), appleTree);        
    }
 
    @Test
    public void stubbingWithAnswerAlias() {
        when(tree.grow(isA(Seed.class))).then(new ReturnTree());
        
        assertEquals(tree.grow(seed), appleTree);        
    }
    
    private class ReturnTree implements Answer {

        public Tree answer(InvocationOnMock invocation) throws Throwable {
            Seed seed = invocation.getArgumentAt(0, Seed.class);
            return seed.getName().equals("Apple") ? appleTree : tree;
        }
        
    }
        
    @Test
    public void stubbingWithConsecuitiveAnswers() {
        AppleTree redAppleTree = mock(AppleTree.class);
        when(redAppleTree.getName()).thenReturn("Red Apple Tree");
        
        final AppleTree greenAppleTree = mock(AppleTree.class);
        when(greenAppleTree.getName()).thenReturn("Green Apple Tree");
        
        when(appleTree.grow(appleSeed)).thenAnswer(new Answer() {

            public Tree answer(InvocationOnMock invocation) throws Throwable {
                return appleTree;
            }
        })
        .thenReturn(redAppleTree)
        .thenAnswer(new Answer() {
            public AppleTree answer(InvocationOnMock invocation) throws Throwable {
                return greenAppleTree;
            }
        });;
        
        assertEquals(appleTree.grow(appleSeed), appleTree);
        assertEquals(appleTree.grow(appleSeed), redAppleTree);  
        assertEquals(appleTree.grow(appleSeed), greenAppleTree);  
    }   
}

Stubbing with Answer Callback for a Void Method

Since a void method doesn't return anything, the stubbing starts with doAnswer() and then follows when(mock(). The final call is mock object's void method as it doesn't return anything.

package com.javarticles.mockito;

import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.*;
import static org.testng.Assert.assertEquals;

import org.mockito.exceptions.misusing.CannotVerifyStubOnlyMock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class StubbingWithAnswersTests {
    private Tree tree;
    private Seed seed = new Seed("Apple");
    private AppleTree appleTree;
    private AppleSeed appleSeed = new AppleSeed();
    
    @BeforeMethod
    public void createMock() {
        tree = mock(Tree.class);
        seed.setTree(tree);
        appleTree = mock(AppleTree.class);
        appleSeed.setTree(appleTree);
        when(appleTree.getName()).thenReturn("Apple Tree");
    }
    
    @Test
    public void stubbingWithAnswer() {
        when(tree.grow(isA(Seed.class))).thenAnswer(new Answer() {

            public Tree answer(InvocationOnMock invocation) throws Throwable {
                Seed seed = invocation.getArgumentAt(0, Seed.class);
                return seed.getName().equals("Apple") ? appleTree : tree;
            }
        });
        
        assertEquals(tree.grow(seed), appleTree);        
    }
 
    @Test
    public void stubbingWithAnswerAlias() {
        when(tree.grow(isA(Seed.class))).then(new ReturnTree());
        
        assertEquals(tree.grow(seed), appleTree);        
    }
    
    private class ReturnTree implements Answer {

        public Tree answer(InvocationOnMock invocation) throws Throwable {
            Seed seed = invocation.getArgumentAt(0, Seed.class);
            return seed.getName().equals("Apple") ? appleTree : tree;
        }
        
    }
        
    @Test
    public void stubbingWithConsecuitiveAnswers() {
        AppleTree redAppleTree = mock(AppleTree.class);
        when(redAppleTree.getName()).thenReturn("Red Apple Tree");
        
        final AppleTree greenAppleTree = mock(AppleTree.class);
        when(greenAppleTree.getName()).thenReturn("Green Apple Tree");
        
        when(appleTree.grow(appleSeed)).thenAnswer(new Answer() {

            public Tree answer(InvocationOnMock invocation) throws Throwable {
                return appleTree;
            }
        })
        .thenReturn(redAppleTree)
        .thenAnswer(new Answer() {
            public AppleTree answer(InvocationOnMock invocation) throws Throwable {
                return greenAppleTree;
            }
        });;
        
        assertEquals(appleTree.grow(appleSeed), appleTree);
        assertEquals(appleTree.grow(appleSeed), redAppleTree);  
        assertEquals(appleTree.grow(appleSeed), greenAppleTree);  
    }   
    
    @Test
    public void answerVoidMethod() {
        final int fruitsCount = 20;
        final int fruitsPlucked = 4;
        final int[] remainingFruits = new int[1];
        doAnswer(new Answer(){

            public Integer answer(InvocationOnMock invocation) throws Throwable {
                remainingFruits[0] = fruitsCount - fruitsPlucked;
                return remainingFruits[0];
            }}).when(appleTree).pluckFruits();
        appleTree.pluckFruits();
        assertEquals(remainingFruits[0], 16);
    }
}

Consecutive Stubbing with Answer Callback for a Void Method

One can stub with multiple doAnswer() so that for each call of the void method, you can recompute the answer logic. Even in this case, the final call is mock object's void method.

package com.javarticles.mockito;

import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.*;
import static org.testng.Assert.assertEquals;

import org.mockito.exceptions.misusing.CannotVerifyStubOnlyMock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class StubbingWithAnswersTests {
    private Tree tree;
    private Seed seed = new Seed("Apple");
    private AppleTree appleTree;
    private AppleSeed appleSeed = new AppleSeed();
    
    @BeforeMethod
    public void createMock() {
        tree = mock(Tree.class);
        seed.setTree(tree);
        appleTree = mock(AppleTree.class);
        appleSeed.setTree(appleTree);
        when(appleTree.getName()).thenReturn("Apple Tree");
    }
    
    @Test
    public void stubbingWithAnswer() {
        when(tree.grow(isA(Seed.class))).thenAnswer(new Answer() {

            public Tree answer(InvocationOnMock invocation) throws Throwable {
                Seed seed = invocation.getArgumentAt(0, Seed.class);
                return seed.getName().equals("Apple") ? appleTree : tree;
            }
        });
        
        assertEquals(tree.grow(seed), appleTree);        
    }
 
    @Test
    public void stubbingWithAnswerAlias() {
        when(tree.grow(isA(Seed.class))).then(new ReturnTree());
        
        assertEquals(tree.grow(seed), appleTree);        
    }
    
    private class ReturnTree implements Answer {

        public Tree answer(InvocationOnMock invocation) throws Throwable {
            Seed seed = invocation.getArgumentAt(0, Seed.class);
            return seed.getName().equals("Apple") ? appleTree : tree;
        }
        
    }
        
    @Test
    public void stubbingWithConsecuitiveAnswers() {
        AppleTree redAppleTree = mock(AppleTree.class);
        when(redAppleTree.getName()).thenReturn("Red Apple Tree");
        
        final AppleTree greenAppleTree = mock(AppleTree.class);
        when(greenAppleTree.getName()).thenReturn("Green Apple Tree");
        
        when(appleTree.grow(appleSeed)).thenAnswer(new Answer() {

            public Tree answer(InvocationOnMock invocation) throws Throwable {
                return appleTree;
            }
        })
        .thenReturn(redAppleTree)
        .thenAnswer(new Answer() {
            public AppleTree answer(InvocationOnMock invocation) throws Throwable {
                return greenAppleTree;
            }
        });;
        
        assertEquals(appleTree.grow(appleSeed), appleTree);
        assertEquals(appleTree.grow(appleSeed), redAppleTree);  
        assertEquals(appleTree.grow(appleSeed), greenAppleTree);  
    }   
    
    @Test
    public void answerVoidMethod() {
        final int fruitsCount = 20;
        final int fruitsPlucked = 4;
        final int[] remainingFruits = new int[1];
        doAnswer(new Answer(){

            public Integer answer(InvocationOnMock invocation) throws Throwable {
                remainingFruits[0] = fruitsCount - fruitsPlucked;
                return remainingFruits[0];
            }}).when(appleTree).pluckFruits();
        appleTree.pluckFruits();
        assertEquals(remainingFruits[0], 16);
    }
    
    @Test
    public void answerVoidMethodConsecutively() {
        final int fruitsCount = 20;
        final int fruitsPlucked = 4;
        final int[] remainingFruits = new int[1];
        
        doAnswer(new Answer(){

            public Integer answer(InvocationOnMock invocation) throws Throwable {
                remainingFruits[0] = fruitsCount - fruitsPlucked;
                return remainingFruits[0];
            }}).
            doAnswer(new Answer(){

                public Integer answer(InvocationOnMock invocation) throws Throwable {
                    remainingFruits[0] = remainingFruits[0] - fruitsPlucked;
                    return remainingFruits[0];
                }}).
           doThrow(new RuntimeException("You can't pluck any more fruits!"))     
           .when(appleTree).pluckFruits();
        
        appleTree.pluckFruits();
        assertEquals(remainingFruits[0], 16);
        
        appleTree.pluckFruits();
        assertEquals(remainingFruits[0],12);
        
        try {
            appleTree.pluckFruits();
            Assert.fail();
        } catch(RuntimeException e) {
            
        }
    }
}

Download the source code

This was an example about Mockito Stubbing using Answer Callback.

You can download the source code here: mockitoStubbingWithAnswers
Share.

Comments are closed.