Testing that an entity doesn’t get modified (no setters called) with mockito

I have recently started using mockito to improve the quality of my unit tests. It’s a great tool, with a very clear syntax that makes tests very readable. Also, if you follow a BDD pattern in your tests, mockito has aliases in BDDMockito so that your actions can clearly follow the given/then/when template.

I’m using it to test the behaviour of some transactional services. The underlying persistence technology used is JPA, so checking that some entity is not modified is a bit hard. Why? Because with JPA, the usual way of updating an entity is by bringing it into your persistence context, modifying it, and then simply closing the context. The entity manager will detect that the retrieved entity has changed, and will create the appropriate update statement.

So, if I want to ensure that a method in the service layer doesn’t modify an entity, a clean way of doing it is ensuring that no setter methods are called on that entity. In order to be able to test this with mockito, you need to do two things: first, make the mocked DAO return a “spy” of the entity; and second, verify that no spurious set* methods have been called on that spy during the service call.

The fist part is the easy one, simply create the entity that should be returned, and once it has all of its expected values set, wrap it in a spy.

MyEntity entity = new MyEntity();
entity.setId(1);
entity.setText("hello");
entity = spy(entity);
given(mockedEntityDao.findEntityById(1)).willReturn(entity);

The second part is trickier. A new verification mode needs to be created. Its verify method will have access to the list of all the called methods in the spy, and it’s simply a matter of checking that no method that has the signature of a setter (its name matches the expression set[A-Z].* and has 1 parameter) has been called. The code of this verification mode is as follows:

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.mockito.exceptions.Reporter;
import org.mockito.internal.invocation.Invocation;
import org.mockito.internal.verification.api.VerificationData;
import org.mockito.internal.verification.api.VerificationMode;
 
/**
 * Mockito verification mode to ensure that no setter calls have been performed
 * on a mock/spy.
 * 
 * @author Carlos Vara
 */
public class NoSetterCalls implements VerificationMode {
 
    private final Reporter reporter;
    private final Pattern regexPattern;
 
    public NoSetterCalls() {
        this.reporter = new Reporter();
        this.regexPattern = Pattern.compile("set[A-Z].*");
    }
 
    @Override
    public void verify(VerificationData data) {
 
        List<Invocation> invocations = data.getAllInvocations();
        for ( Invocation inv : invocations ) {
 
            Matcher m = this.regexPattern.matcher(inv.getMethodName());
            if ( m.matches() && inv.getArgumentsCount() == 1 ) {
                // A setter has been called!
                this.reporter.neverWantedButInvoked(inv, inv.getLocation());
            }
 
        }
 
    }
 
}

And finally, an example test would be as follows:

@Test
public void checkSimpleAlternativeParagraph() {
 
    // GIVEN
    //  - A valid call to a service methodproposal to create a new alternative
    String callParam = "1";
    //  - And the expected DAO behaviour
    MyEntity entity = new MyEntity();
    entity.setId(1);
    entity.setText("hello");
    entity = spy(entity);
    given(mockedEntityDao.findEntityById(1)).willReturn(entity);
 
    // WHEN
    this.entityService.doSomethingButDontModify(callParam);
 
    // THEN
    //  - Verify that the queried entity hasn't been modified
    verify(entity, new NoSetterCalls()).setId("this setId() call is ignored");
 
}

And it works. The only drawback, is that you have to complete the verification call with an extra method call, the setId() call in this case, but could be any other method. This call has no side-effects (it gets passed as parameter to the NoSetterCalls verifier) but disturbs a little the clarity of the test.

Note when using JPA2

In JPA2, the entity manager has a detach method. If your application uses it, the way to test that no modification is performed would be to check that no setters followed by a flush are performed in the entity prior to calling detach. Or more easily, add a DAO method that returns a detached entity, and move this check to the DAO.