ChatGPT解决这个技术问题 Extra ChatGPT

Making a mocked method return an argument that was passed to it

Consider a method signature like:

public String myFunction(String abc);

Can Mockito help return the same string that the method received?

Ok, how about any java mocking framework in general... Is this possible with any other framework, or should I just create a dumb stub to mimic the behavior I want?

T
Top-Master

Since Mockito 1.9.5+ and Java 8+

You can use a lambda expression, like:

when(myMock.myFunction(anyString())).thenAnswer(i -> i.getArguments()[0]);

Where i is an instance of InvocationOnMock.

For older versions

You can create an Answer in Mockito. Let's assume, we have an interface named MyInterface with a method myFunction.

public interface MyInterface {
    public String myFunction(String abc);
}

Here is the test method with a Mockito answer:

public void testMyFunction() throws Exception {
    MyInterface mock = mock(MyInterface.class);
    when(mock.myFunction(anyString())).thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
        Object[] args = invocation.getArguments();
        return (String) args[0];
    }
    });

    assertEquals("someString",mock.myFunction("someString"));
    assertEquals("anotherString",mock.myFunction("anotherString"));
}

This is what I was looking for, too. Thank you! My problem was different, though. I want to mock a persistence service (EJB) that stores objects and returns them by name.
I created an extra class that wraps the creation of the answer. So the code reads like when(...).then(Return.firstParameter())
With Java 8 lambdas it's supper easy to return first argument, even for specific class, i.e. when(foo(any()).then(i -> i.getArgumentAt(0, Bar.class)). And you can just as well use a method reference and call real method.
This solves my problem with a method the returns Iterator<? extends ClassName> which causes all kinds of cast problems in a thenReturn() statement.
With Java 8 and Mockito < 1.9.5 then Paweł's answer becomes when(foo(any()).thenAnswer(i -> i.getArguments()[0])
A
Abdull

If you have Mockito 1.9.5 or higher, there is a new static method that can make the Answer object for you. You need to write something like

import static org.mockito.Mockito.when;
import static org.mockito.AdditionalAnswers.returnsFirstArg;

when(myMock.myFunction(anyString())).then(returnsFirstArg());

or alternatively

doAnswer(returnsFirstArg()).when(myMock).myFunction(anyString());

Note that the returnsFirstArg() method is static in the AdditionalAnswers class, which is new to Mockito 1.9.5; so you'll need the right static import.


Note: it's when(...).then(returnsFirstArg()), I mistakenly had when(...).thenReturn(returnsFirstArg()) which gave java.lang.ClassCastException: org.mockito.internal.stubbing.answers.ReturnsArgumentAt cannot be cast to
Note: returnsFirstArg() returns Answer<> rather than the value of the argument. Got 'Foo(java.lang.String) cannot be applied to '(org.mockito.stubbing.Answer)' while trying to call .thenReturn(new Foo(returnsFirstArg()))
I always need to google this answer again and again and again for the past years, as I just can't remember "AdditionalAnswers" and I just need it very rarely. Then I wonder how the heck I can built up that scenario as I can't find the necessary dependencies. Couldn't this just be added directly to mockito? :/
Steve's answer is more generic. This one only allows you to return the raw argument. If you want to process that argument and return the result, then Steve's answer rules. I upvoted both as they are both useful.
FYI, we have to import static org.mockito.AdditionalAnswers.returnsFirstArg. this to use returnsFirstArg. Also, I can do when(myMock.myFunction(any())).then(returnsFirstArg()) in Mockito 2.20.*
P
Paweł Dyda

With Java 8 it is possible to create a one-line answer even with older version of Mockito:

when(myMock.myFunction(anyString()).then(i -> i.getArgumentAt(0, String.class));

Of course this is not as useful as using AdditionalAnswers suggested by David Wallace, but might be useful if you want to transform argument "on the fly".


Brilliant. Thank you. If the argument is long, can this still work with boxing and Long.class ?
.getArgumentAt(..) was not found for me but .getArgument(1) worked (mockito 2.6.2)
m
migu

I had a very similar problem. The goal was to mock a service that persists Objects and can return them by their name. The service looks like this:

public class RoomService {
    public Room findByName(String roomName) {...}
    public void persist(Room room) {...}
}

The service mock uses a map to store the Room instances.

RoomService roomService = mock(RoomService.class);
final Map<String, Room> roomMap = new HashMap<String, Room>();

// mock for method persist
doAnswer(new Answer<Void>() {
    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Object[] arguments = invocation.getArguments();
        if (arguments != null && arguments.length > 0 && arguments[0] != null) {
            Room room = (Room) arguments[0];
            roomMap.put(room.getName(), room);
        }
        return null;
    }
}).when(roomService).persist(any(Room.class));

// mock for method findByName
when(roomService.findByName(anyString())).thenAnswer(new Answer<Room>() {
    @Override
    public Room answer(InvocationOnMock invocation) throws Throwable {
        Object[] arguments = invocation.getArguments();
        if (arguments != null && arguments.length > 0 && arguments[0] != null) {
            String key = (String) arguments[0];
            if (roomMap.containsKey(key)) {
                return roomMap.get(key);
            }
        }
        return null;
    }
});

We can now run our tests on this mock. For example:

String name = "room";
Room room = new Room(name);
roomService.persist(room);
assertThat(roomService.findByName(name), equalTo(room));
assertNull(roomService.findByName("none"));

C
Community

With Java 8, Steve's answer can become

public void testMyFunction() throws Exception {
    Application mock = mock(Application.class);
    when(mock.myFunction(anyString())).thenAnswer(
    invocation -> {
        Object[] args = invocation.getArguments();
        return args[0];
    });

    assertEquals("someString", mock.myFunction("someString"));
    assertEquals("anotherString", mock.myFunction("anotherString"));
}

EDIT: Even shorter:

public void testMyFunction() throws Exception {
    Application mock = mock(Application.class);
    when(mock.myFunction(anyString())).thenAnswer(
        invocation -> invocation.getArgument(0));

    assertEquals("someString", mock.myFunction("someString"));
    assertEquals("anotherString", mock.myFunction("anotherString"));
}

That's cool, but it doesn't work for thenThrow, unfortunately (thenThrow takes no InvocationOnMock argument).
L
LazR

This is a pretty old question but i think still relevant. Also the accepted answer works only for String. Meanwhile there is Mockito 2.1 and some imports have changed, so i would like to share my current answer:

import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@Mock
private MyClass myClass;

// this will return anything you pass, but it's pretty unrealistic
when(myClass.myFunction(any())).then(returnsFirstArg());
// it is more "life-like" to accept only the right type
when(myClass.myFunction(any(ClassOfArgument.class))).then(returnsFirstArg());

The myClass.myFunction would look like:

public class MyClass {
    public ClassOfArgument myFunction(ClassOfArgument argument){
        return argument;
    }  
}

C
Cyril Cherian

You can achieve this by using ArgumentCaptor

Imagine you have bean function like so.

public interface Application {
  public String myFunction(String abc);
}

Then in your test class:

//Use ArgumentCaptor to capture the value
ArgumentCaptor<String> param = ArgumentCaptor.forClass(String.class);


when(mock.myFunction(param.capture())).thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
      return param.getValue();//return the captured value.
    }
  });

OR if you fan of lambda simply do:

//Use ArgumentCaptor to capture the value
ArgumentCaptor<String> param = ArgumentCaptor.forClass(String.class);


when(mock.myFunction(param.capture()))
    .thenAnswer((invocation) -> param.getValue());

Summary: Use argumentcaptor, to capture the parameter passed. Later in answer return the value captured using getValue.


This doesn´t work (anymore?). Regarding to the docs: This method must be used inside of verification. That means you can only capture the value when using the verify method
1. Not sure what you mean by This doesn´t work (anymore?). I have this working on my instance. 2. Sorry, I am not clear on the point you trying to make. The answer is specific to OP's question.
L
Lachezar Balev

This is a bit old, but I came here because I had the same issue. I'm using JUnit but this time in a Kotlin app with mockk. I'm posting a sample here for reference and comparison with the Java counterpart:

@Test
fun demo() {
  // mock a sample function
  val aMock: (String) -> (String) = mockk()

  // make it return the same as the argument on every invocation
  every {
    aMock.invoke(any())
  } answers {
    firstArg()
  }

  // test it
  assertEquals("senko", aMock.invoke("senko"))
  assertEquals("senko1", aMock.invoke("senko1"))
  assertNotEquals("not a senko", aMock.invoke("senko"))
}

f
fl0w

You might want to use verify() in combination with the ArgumentCaptor to assure execution in the test and the ArgumentCaptor to evaluate the arguments:

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
verify(mock).myFunction(argument.capture());
assertEquals("the expected value here", argument.getValue());

The argument's value is obviously accessible via the argument.getValue() for further manipulation / checking /whatever.


m
martin

I use something similar (basically it's the same approach). Sometimes it's useful to have a mock object return pre-defined output for certain inputs. That goes like this:

private Hashtable<InputObject,  OutputObject> table = new Hashtable<InputObject, OutputObject>();
table.put(input1, ouput1);
table.put(input2, ouput2);

...

when(mockObject.method(any(InputObject.class))).thenAnswer(
       new Answer<OutputObject>()
       {
           @Override
           public OutputObject answer(final InvocationOnMock invocation) throws Throwable
           {
               InputObject input = (InputObject) invocation.getArguments()[0];
               if (table.containsKey(input))
               {
                   return table.get(input);
               }
               else
               {
                   return null; // alternatively, you could throw an exception
               }
           }
       }
       );