Using The New Mockito Annotations

Posted by: James Carr on March 9, 2010

It’s only been a few days since the Mockito 1.8.3 release and I have to say that for a minor release there’s a lot I like about it!

What I like about the new annotations is the ability to have test cases that are completely free of @Before. Observe this example of a class that takes a text input string, translates it to an AuctionEvent, and fires it off to an AuctionEventListener:

@RunWith(MockitoJUnitRunner.class)
public class AuctionMessageTranslatorTest {
	@Mock AuctionEventListener listener;
	@InjectMocks AuctionMessageTranslator translator = new AuctionMessageTranslator();

	@Test
	public void shouldSendAnEventToTheListener(){
		translator.sendMessage("SOL Version: 1.1; Event: PRICE;");

		verify(listener).handleEvent(any(AuctionEvent.class));
	}
}

This is a first step, to just verify an event is passed to the listener (we don’t care about it’s contents yet). @Mock creates a mock on each test method run, and @InjectMocks will pass mocks to any matching setters or constructors.

Now I’ll implement a little code to make the example pass.

public class AuctionMessageTranslator {
	private AuctionEventListener listener;
	public void setListener(AuctionEventListener listener) {
		this.listener = listener;
	}

	public void sendMessage(String message) {
		listener.handleEvent(new AuctionEvent());
	}
}

Doesn’t do much… so let’s add a new example that verifies the contents of the message sent to the listener. Since this object is created by the translator (translating a string to an object) we’ll use an argument captor to capture and verify it’s value.

@RunWith(MockitoJUnitRunner.class)
public class AuctionMessageTranslatorTest {
	@Mock AuctionEventListener listener;
	@Captor ArgumentCaptor<AuctionEvent> arg;
	@InjectMocks AuctionMessageTranslator translator = new AuctionMessageTranslator();

	@Test
	public void shouldSendAnEventToTheListener(){
		translator.sendMessage("SOL Version: 1.1; Event: PRICE;");

		verify(listener).handleEvent(any(AuctionEvent.class));
	}

	@Test
	public void shouldSendAnEventWithNamePrice(){
		translator.sendMessage("SOL Version: 1.1; Event: PRICE;");

		verify(listener).handleEvent(arg.capture());

		assertThat(arg.getValue().getName(), equalTo("PRICE"));
	}
}

It fails, so we implement the code to make it pass:

	public void sendMessage(String message) {
		listener.handleEvent(parseEvent(message));
	}

	private AuctionEvent parseEvent(String message) {
		AuctionEvent auctionEvent = new AuctionEvent();
		auctionEvent.setName(message.split(";")[1].split(":")[1].trim());
		return auctionEvent;
	}

This passes as the argument passed to the listener does indeed contain the event name. This is a little ugly, so let’s refactor it a little bit with our test providing a nice safety net:

	public void sendMessage(String message) {
		listener.handleEvent(parseEvent(message));
	}

	private AuctionEvent parseEvent(String message) {
		AuctionEvent auctionEvent = new AuctionEvent();
		auctionEvent.setName(unpackMessage(message).get("Event"));
		return auctionEvent;
	}

	private Map<String, String> unpackMessage(String message) {
		Map<String, String> pairs = new HashMap<String, String>();
		for(String pairString : message.split(";")){
			String[] pair = pairString.split(":");
			pairs.put(pair[0].trim(), pair[1].trim());
		}
		return pairs;
	}

Looks good, and the test case for it is pretty clean although it has a lot of annotations. We could change the injection strategy to use constructor injection since we don’t really want the object to even exist without a listener:

@RunWith(MockitoJUnitRunner.class)
public class AuctionMessageTranslatorTest {
	@Mock AuctionEventListener listener;
	@Captor ArgumentCaptor<AuctionEvent> arg;
	@InjectMocks AuctionMessageTranslator translator = new AuctionMessageTranslator(listener);
...
}

As long as the @InjectMocks annotation is present, the MockitoJunitRunner will initialize the mocks do they’re available for injection. Drop the @InjectMocks annotation off, and it fails with a null pointer exception.

One interesting thing of note when using @InjectMocks with setter injection is if you do something silly like the following:

@RunWith(MockitoJUnitRunner.class)
public class AuctionMessageTranslatorTest {
	@Mock AuctionEventListener listener;
	@Mock AuctionEventListener listener2;
	@Captor ArgumentCaptor>AuctionEvent> arg;
	@InjectMocks AuctionMessageTranslator translator = new AuctionMessageTranslator();
	...
}

It will inject the first @Mock, not the second. Verifications against listener will work, while verifications against listener2 will fail as it was never injected.

Tomorrow I’ll include some examples of using the @Spy annotation as well as the different answer types you can configure @Mock annotated mocks with as of 1.8.3. ;)

James Carr

About James Carr

James is a contractor in the St.Louis area that shares a passion for software craftsmanship and has enjoyed software development since he wrote his first program in Basic on the Tandy Color Computer 3 way back in 1988.

In addition to a passion for technology, he also has a keen interest in improving teamwork and collaboration through interactive activities to get people thinking creatively and develop stronger, richer communication channels with their stakeholders.