21

I want to stub a repository class to test another class (Holder class) that has a repository. The repository interface supports CRUD operations, and has many methods, but my unit test on the Holder class only needs to call two of them. The repository interface:

public interface IRepo {
    public void remove(String... sarr);

    public void add(String... sarr);

    //Lots of other methods I don't need now
}

I want to create a repository mock that can store instances, define logic for add and remove only, and also provide a means of checking what is stored on it after calling add and remove.

If I do:

IRepo repoMock = mock(IRepo.class);

Then I have a dumb object that does nothing on each method. That's OK, now I just need to define behaviour for add and remove.

I could create a Set<String> and stub only those two methods to work on the set. Then I'd instantiate a Holder that has an IRepo, inject the partially stubbed mock, and after exercising the holder, check the set to verify it contains what it should.

I've managed to partially stub a void method like remove using the deprecated method stubVoid:

Set<String> mySet = new HashSet<>();
stubVoid(repoMock).toAnswer(new Answer<Void>() {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Object[] args = invocation.getArguments();
        String[] stringsToDelete = (String[]) args[0];
        mySet.removeAll(Arrays.asList(stringsToDelete));
        return null;
    }
}).on().remove(Matchers.<String>anyVararg());

But is deprecated, and it is not much better than creating a partial implementation for IRepo. Is there a better way?

NOTE: Java 7 answers only please, this should run in Android.

Mister Smith
  • 27,417
  • 21
  • 110
  • 193

4 Answers4

27

You can use

  Mockito.doAnswer(new Answer<Void>() {
        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            //DO SOMETHING
            return null;
        }
    }).when(...).remove(Matchers.<String>anyVararg());

From the Javadoc:

Use doAnswer() when you want to stub a void method with generic Answer.

Stubbing voids requires different approach from Mockito.when(Object) because the compiler does not like void methods inside brackets...

Example:

    doAnswer(new Answer() {
    public Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        Mock mock = invocation.getMock();
        return null;
    }}).when(mock).someMethod();

See examples in javadoc for Mockito

Saminda Peramuna
  • 735
  • 9
  • 22
Jens
  • 67,715
  • 15
  • 98
  • 113
  • Interesting how there's no `doSomething` accepting a Runnable instead of having to hack a void answer. Is my use case so far fetched it doesn't deserve to be in Mockito's API? – Mister Smith Aug 21 '15 at 12:13
  • I doubt there is any library out there satisfying all users. Mockito is very easy to extend (see my answer) for almost any purpose. A Runnable by the way would not work in your example, as you need the invocation arguments. – Ruben Aug 22 '15 at 20:05
  • Fixed my issue of trying to forcefully change some data midway through a test, thank you! – EM-Creations Nov 09 '17 at 11:08
5

Suppose you have

public class IFace {
    public void yourMethod() {            
    }
}

Then to mock it you need

    IFace mock = Mockito.mock(IFace.class);
    Mockito.doAnswer(new Answer() {
        @Override
        public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
            //PUT YOUR CODE HERE
            return null;
        }
    }).when(mock).yourMethod();
talex
  • 17,973
  • 3
  • 29
  • 66
1

If you really don't like to return null in your implementation of Answer, you can create your own Answer implementation that delegates to a void method:

public abstract class DoesSomethingAnswer implements Answer<Void> {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        doSomething(invocation);
        return null;
    }

    protected abstract void doSomething(InvocationOnMock invocation);    
}

Then your test has one line less:

    Set<String> mySet = new HashSet<>();
    Mockito.doAnswer(new DoesSomethingAnswer() {
        @Override
        protected void doSomething(InvocationOnMock invocation) {
            Object[] args = invocation.getArguments();
            String[] stringsToDelete = (String[]) args[0];
            mySet.removeAll(Arrays.asList(stringsToDelete));
        }
    }).when(repoMock).remove(Matchers.<String> anyVararg());

Or, if you just need the arguments from the invocation:

public abstract class DoesSomethingAnswer implements Answer<Void> {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        doSomething(invocation.getArguments());
        return null;
    }

    protected abstract void doSomething(Object[] args);
}
Ruben
  • 3,986
  • 1
  • 21
  • 34
1

In addition to https://stackoverflow.com/a/32139397/4471199 you can use lambda:

Mockito.doAnswer(
        invocation -> {
            // DO SOMETHING
            return null;
        }
).when(repoMock).remove(Matchers.<String>anyVararg());
RoBeaToZ
  • 1,113
  • 10
  • 18