Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mockito: Is there a way of capturing the return value of stubbed method?

Tags:

If I mock a method to return a new instance of some object, how can I capture the returned instance?

E.g.:

 when(mock.someMethod(anyString())).thenAnswer(new Answer() {      Object answer(InvocationOnMock invocation) {          Object[] args = invocation.getArguments();          Object mock = invocation.getMock();          return new Foo(args[0])      }  }); 

Obviously, I can have a field of type Foo and inside answer set it to the new instance, but is there a nicer way? Something like ArgumentCaptor?

like image 899
IttayD Avatar asked Aug 31 '11 08:08

IttayD


People also ask

How do you return a mock value?

You can return values from mocked methods by using the returnValue action within the " will " clause of an expectation. oneOf (calculator). add(2, 2); will(returnValue(5)); jMock will fail the test if you try to return a value of the wrong type.

What does stub do in Mockito?

A stub is a fake class that comes with preprogrammed return values. It's injected into the class under test to give you absolute control over what's being tested as input. A typical stub is a database connection that allows you to mimic any scenario without having a real database.

How do I return an object in Mockito?

In Mockito, you can specify what to return when a method is called. That makes unit testing easier because you don't have to change existing classes. Mockito supports two ways to do it: when-thenReturn and doReturn-when . In most cases, when-thenReturn is used and has better readability.


2 Answers

I wanted to do something similar, but with a spied object rather than a mock. Specifically, given a spied object, I want to capture the return value. Based on Andreas_D's answer, here's what I came up with.

public class ResultCaptor<T> implements Answer {     private T result = null;     public T getResult() {         return result;     }      @Override     public T answer(InvocationOnMock invocationOnMock) throws Throwable {         result = (T) invocationOnMock.callRealMethod();         return result;     } } 

Intended usage:

// spy our dao final Dao spiedDao = spy(dao); // instantiate a service that does some stuff, including a database find final Service service = new Service(spiedDao);  // let's capture the return values from spiedDao.find() final ResultCaptor<QueryResult> resultCaptor = new ResultCaptor<>(); doAnswer(resultCaptor).when(spiedDao).find(any(User.class), any(Query.class));  // execute once service.run(); assertThat(resultCaptor.getResult()).isEqualTo(/* something */);  /// change conditions ///  // execute again service.run(); assertThat(resultCaptor.getResult()).isEqualTo(/* something different */); 
like image 111
Jeff Fairley Avatar answered Oct 01 '22 15:10

Jeff Fairley


Looks like you want to observe and then Answer instances, and receive notifications each time the answer method is called (which triggers the creation of a new Foo). So why not invent an ObservableAnswer class:

public abstract class ObservableAnswer implements Answer {   private Listener[] listeners; // to keep it very simple...    public ObservableAnswer(Listener...listeners) {     this.listeners = listeners;   }    @Override   public Object answer(InvocationOnMock invocation) {     Object answer = observedAnswer(invocation);     for (Listener listener:listeners) {        listener.send(answer);     }     return answer;   }    // we'll have to implement this method now   public abstract Object observedAnswer(InvocationOnMock invocation); } 

Intended use:

Listener[] myListeners = getListeners();  // some magic (as usual) when(mock.someMethod(anyString())).thenAnswer(new ObservableAnswer(myListeners) {      Object observedAnswer(InvocationOnMock invocation) {          Object[] args = invocation.getArguments();          Object mock = invocation.getMock();          return new Foo(args[0])      }   }); 
like image 36
Andreas Dolk Avatar answered Oct 01 '22 16:10

Andreas Dolk