How to make a mocked method return an argument that was passed to it using Mockito

November 27, 2020 1 Comment Mockito Java Mock Answer

1. Introduction

This article will cover one of the common problems we could face while writing unit tests which is making mocked method return an argument that was passed to that method. We will present several solutions that could be used to achieve that.

2. Stubbing with callbacks in Mockito

With Mockito we are able to get full control of the mocked method with stubbing. When we want to condition the returned parameter with what we get on the input we could use stubbing with a generic Answer interface.

Let's check the example:

package com.frontbackend.libraries.mockito;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class MockitoAnswerTest {

    interface SomeInterface {
        String doSomething(String param);
    }

    @Mock
    SomeInterface someInterface;

    @Test
    public void testThenAnswer() {
        when(someInterface.doSomething(anyString())).thenAnswer(invocation -> invocation.getArguments()[0]);

        assertEquals("someString", someInterface.doSomething("someString"));
        assertEquals("anotherString", someInterface.doSomething("anotherString"));
    }
}

In this code we created a mock object with SomeInterface type. The statement when(someInterface.doSomething(anyString())).thenAnswer(...) allows us to stub a method doSomethig and additionally gives the ability to check/manipulate input parameters. We want to return the same parameter we get as a first argument thats why we used invocation.getArguments()[0] statement.

3. Using returnsFirstArg() method

The doAnswer(...).when(...).[method(...)] is another method that we could use to create a mock object with a method that will return a parameter that was passed to it.

In the following example instead of using thenAnswer(invocation -> invocation.getArguments()[0]) like in the previous code we simply make a use of returnsFirstArg() method that will do the same but clearly:

package com.frontbackend.libraries.mockito;

import static org.junit.Assert.assertEquals;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class MockitoAnswerTest {

    interface SomeInterface {
        String doSomething(String param);
    }

    @Mock
    SomeInterface someInterface;

    @Test
    public void testThenReturn() {
        Mockito.doAnswer(returnsFirstArg())
               .when(someInterface)
               .doSomething(anyString());

        assertEquals("someString", someInterface.doSomething("someString"));
        assertEquals("anotherString", someInterface.doSomething("anotherString"));
    }
}

We could also use when(...).then(...) notation with returnsFirstArg() method.

    @Test
    public void testReturnsFirstArg() {
        when(someInterface.doSomething(anyString())).then(returnsFirstArg());
        assertEquals("someString", someInterface.doSomething("someString"));
        assertEquals("anotherString", someInterface.doSomething("anotherString"));
    }
}

4. Conclusion

In this example, we presented how to create a mock object with a method that will return the first parameter given to that method. Please note that Mockito officially wrote that thenAnswer(...) is a controversial feature that was not included in Mockito originally. Note that Mockito recommends using thenReturn() and/or thenThow() which should be enough to write clean and simple tests.

The code used in this tutorial is available under GitHub repository

{{ message }}

{{ 'Comments are closed.' | trans }}