Test your code

Power Mocks

Mockito is cool, but in some cases workarounds are required to resolve certain situations. These workarounds force you to alter your code is order to be testable, but for a person who reads the code may find some of it not that useful, until he is explained why it was done this way. I will describe some of the cases I’m referring to.

Constructor calls

How many times do we construct objects using new constructor? Almost every time. In mockito usually a workaround is required in order to test this. The most common case is to build the object through some kind of builder or factory. That way we can just mock the method that builds it, but that builder/factory does not make much sense in the code. In order to be able to test QuoteProvider, instead of just using the constructor call we need to wrap it into a ‘factory’ and call getInstance() on it. When testing we will mock the QuoteProvider and use setInstance() to set the mocked instance.

public class QuoteProvider {

    private static QuoteProvider INSTANCE = new QuoteProvider();

    public static QuoteProvider getInstance() {
        return INSTANCE;
    }

    public void setInstance(QuoteProvider quoteProvider){
       INSTANCE = quoteProvider;
    }

}

As you can see setInstance() method has no real use except for tests.

Static methods and objects

Everyone has these. They are especially used in utilities classes. As we know static means class not instance.

Final methods

Final methods are methods that cannot be overriden. So it’s normal that Mockito cannot create mocks from it. The same goes for final classes.

Private methods

Encapsulation. That’s why we have private keyword. What you can do here is to test the private method in “integration” with the public one that uses it. But not much else. These can be verified when a spy it’s being used.

PowerMock

How can PowerMock help us? It help us in the way it resolves these situations at the byte code level by manipulating it using a custom class loader. So no code changes required. It’s similar in this way with mutation testing. Expectations can be still verified.

Let’s consider this

public class QuotesCentral {

    private MessageServer messageServer = null;
    private final QuoteProvider quoteProvider = QuoteProvider.getInstance();

    public List getQuotes() {
        return quoteProvider.generateQuotes();
    }

    public List findByAuthor(final String author) {
        return getQuotes().stream().filter(q -> q.getAuthor().equals(author))
                                   .collect(Collectors.toList());
    }

    private MessageServer getMessageServer() {
        return Optional.ofNullable(messageServer)
                       .orElseGet(() -> messageServer = new MessageServer());
    }

    private boolean publishQuotes(List quotes) {
        try {
            getMessageServer().connect();
            return getMessageServer().publish(quotes);
        } finally {
            getMessageServer().disconnect();
        }
    }

    public boolean publishQuotesByAuthor(String name) {
        return publishQuotes(findByAuthor(name));
    }

}

We have the setup of the test

@Before
public void setup() throws Exception {
    PowerMockito.mockStatic(QuoteProvider.class);
    Mockito.when(QuoteProvider.getInstance()).thenReturn(quoteProvider);
    Mockito.when(quoteProvider.generateQuotes()).thenReturn(QuoteGenerator.QUOTES);

    quotesCentral = new QuotesCentral();

    PowerMockito.verifyStatic();
    QuoteProvider.getInstance();
}

and one of the test methods

@Test
public void testPublishQuotesByAuthor() throws Exception {
    ArgumentCaptor quotesCaptor = ArgumentCaptor.forClass(List.class);
    String author = "Mark Twain";

    //stub method calls
    PowerMockito.whenNew(MessageServer.class).withNoArguments().thenReturn(messageServer);
    Mockito.doNothing().when(messageServer).connect();
    Mockito.doNothing().when(messageServer).disconnect();
    Mockito.when(messageServer.publish(any(List.class))).thenReturn(true);

    quotesCentral.publishQuotesByAuthor(author);

    //verify interactions
    PowerMockito.verifyNew(MessageServer.class).withNoArguments();
    Mockito.verify(messageServer).connect();
    Mockito.verify(messageServer).publish(quotesCaptor.capture());
    assertThat(quotesCaptor.getValue()).as("There should be 2 quotes").hasSize(2);
    assertThat(quotesCaptor.getValue()).as("The author should be " + author)
              .extracting("author").allMatch(s -> s.equals(author));
    Mockito.verify(messageServer).disconnect();
}

It can be seen where PowerMockito is being used. It’s worth mentioning that each static call should be preceded by a

PowerMockito.verifyStatic();

PowerMock is a powerful tool, but its intended audience are experienced developers who understand mocking. In wrong hands it can do more harm than good. So when deciding to use it or not take a look at the team experience first. This is mainly intended for situations when there’s no dependency injection available. And finally it’s a matter of flavor.

SOURCE CODE

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s