Use Mockito to verify that nothing is called after a method



Answers

Not 100% on topic but I was just looking to find the opposite of verify, and this was the only relevant result, it ends up I was after Mockito.verifyZeroInteractions(mock);

Just incase anyone else ends up here looking for this...

Question

I'm using Mockito to write a unit test in Java, and I'd like to verify that a certain method is the last one called on an object.

I'm doing something like this in the code under test:

row.setSomething(value);
row.setSomethingElse(anotherValue);
row.editABunchMoreStuff();
row.saveToDatabase();

In my mock, I don't care about the order in which I edit everything on the row, but it's very important that I not try to do anything more to it after I've saved it. Is there a good way to do this?

Note that I'm not looking for verifyNoMoreInteractions: it doesn't confirm that saveToDatabase is the last thing called, and it also fails if I call anything on the row that I don't explicitly verify. I'd like to be able to say something like:

verify(row).setSomething(value);
verify(row).setSomethingElse(anotherValue);
verifyTheLastThingCalledOn(row).saveToDatabase();

If it helps, I'm switching to Mockito from a JMock test that did this:

row.expects(once()).method("saveToDatabase").id("save");
row.expects(never()).method(ANYTHING).after("save");



You can access the internals by implementing your own verification mode (thanks to this answer for teaching me, my code is based on it). The implementation is not complete, but you'll get the idea.

Unfortunately Mockito doesn't document this interface, they probably consider it internal (so it might not be 100% stable in future releases).

verify(foo1, new DoFirst()).doThisFirst();
verify(foo2, new DoFirst()).doThisFirst();
verify(foo1, new DoSecond()).beforeDoingThis();
verify(foo1, new DoSecond()).beforeDoingThis();

and then

// Set to true when one 
boolean secondHasHappened = false; 

// Inner classes so they can reach the boolean above.
// Gives an error of any DoSecond verification has happened already.
public class DoFirst implements VerificationMode {
   public void verify(VerificationData data) {
      List<Invocation> invocations = data.getAllInvocations()
      InvocationMatcher matcher = data.getWanted();
      Invocation invocation = invocations.get(invocations.size() - 1);
      if (wanted.matches(invocation) && secondHasHappened) throw new MockitoException("...");
   }
}

// Registers no more DoFirst are allowed to match.
public class DoSecond implements VerificationMode {
    public void verify(VerificationData data) {
       List<Invocation> invocations = data.getAllInvocations()
       InvocationMatcher matcher = data.getWanted();
       Invocation invocation = invocations.get(invocations.size() - 1);
       if (!wanted.matches(invocation)) secondHasHappened = true;
    }
}



Mockito verify no more interactions with any mock

Since verifyNoMoreInteractions take an array of object we have to find a way to get all the created mocks.

You can create this class

public class MocksCollector {
    private final List<Object> createdMocks;

    public MocksCollector() {
        createdMocks = new LinkedList<Object>();
        final MockingProgress progress = new ThreadSafeMockingProgress();
        progress.setListener(new CollectCreatedMocks(createdMocks));
    }

    public Object[] getMocks() {
        return createdMocks.toArray();
    }
}

and then use it in your test :

    public class ATest {
    private final MocksCollector mocksCollector = new MocksCollector();

    @Test
    public void test() throws Exception {
        A a1 = mock(A.class);
        A a2 = mock(A.class);
        A a3 = mock(A.class);
        assertEquals("wrong amount of mocks", 3, mocksCollector.getMocks().length);
        verifyNoMoreInteractions(mocksCollector.getMocks());
        a3.doSomething();
        verifyNoMoreInteractions(mocksCollector.getMocks()); // fail
    }
}

or with annotations :

@RunWith(MockitoJUnitRunner.class)
public class A2Test {
    private final MocksCollector mocksCollector = new MocksCollector();

    @Mock
    private A a1;
    @Mock
    private A a2;
    @Mock
    private A a3;

    @Test
    public void test() throws Exception {
        assertEquals("wrong amount of mocks", 3, mocksCollector.getMocks().length);
        verifyNoMoreInteractions(mocksCollector.getMocks());
        a2.doSomething();
        verifyNoMoreInteractions(mocksCollector.getMocks()); // fail
    }
}

It works but it adds a dependency on mockito internal.




Links



Tags