Gep
Gep

Reputation: 928

Mockito returnsFirstArg() to use

I've started using the Mockito AdditionalAnswers#returnsFirstArg, which is great:

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

but I was wondering if there is an easy way to extract the input argument in order to be used for example in a constructor like:

when(myMock.myFunction(anyString())).thenReturn(new MyObject((String)returnsFirstArg()));

(which obviously doesn't work...)

Upvotes: 12

Views: 6625

Answers (3)

holi-java
holi-java

Reputation: 30696

I think you maybe need a helper class AnswerPipeline that I will introduce it a moment how to make the test more readable, more expressiveness and more interesting!!!

Note: you can transform any Answer to AnswerPipeline by AnswerPipeline#will(Answer) method, not only returnsFirstArg().

THEN using syntax sugar to describe the test, for example:

Function<String, String> function = mock(Function.class);

when(function.apply(anyString())).then(
    /**/ will(returnsFirstArg()) // adapt an Answer to an AnswerPipeline
    /**/.as(String.class)  // set the result type
    /**/.to(String::toUpperCase) // transforming the result
);

assertThat(function.apply("first"), equalTo("FIRST"));

AND then it is easy to solving your problem with no difficulty:

when(myMock.myFunction(anyString()))
           .then(will(returnsFirstArg()).as(String.class).to(MyObject::new));

AnswerPipeline class

interface AnswerPipeline<T> extends Answer<T> {

    static <R> AnswerPipeline<R> will(Answer<R> answer) {
        return answer::answer;
    }

    default <R> AnswerPipeline<R> as(Class<R> type) {
        return to(type::cast);
    }

    default <R> AnswerPipeline<R> to(Function<T, R> mapper) {
        return it -> mapper.apply(answer(it));
    }
}

Upvotes: 5

user7605325
user7605325

Reputation:

You can use thenAnswer method and create an Answer to get the argument:

when(myMock.myFunction(anyString())).thenAnswer(new Answer<MyObject>() {
    @Override
    public MyObject answer(InvocationOnMock invocation) throws Throwable {
        String s = invocation.getArgument(0); // get first argument
        return new MyObject(s);
    }
});

If you're using java 8, you can use lambda syntax:

when(myMock.myFunction(anyString()))
  .thenAnswer(args -> new MyObject(args.getArgument(0)));

Notes:

  • I didn't need to cast invocation.getArgument(0) to String, but depending on your java/mockito version, maybe it'll be necessary: (String) invocation.getArgument(0)
  • depending on your mockito version, the getArgument(int) method might not exist and you should use getArgumentAt(int, Class) instead (in this case, the call would be getArgumentAt(0, String.class)). Or you can use getArguments()[0] and cast it to String

Upvotes: 2

Mureinik
Mureinik

Reputation: 311978

The easiest (only?) approach, IMHO, would be to use the thenAnswer method, which allows you to not only return a value, but actually execute some code. Java 8 makes this particularly elegant, as you could just use an anonymous lambda:

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

Upvotes: 11

Related Questions