Using The New Mockito Annotations
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. ![]()
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.
More About James »Why Attend the NFJS Tour?
- » Cutting-Edge Technologies
- » Agile Practices
- » Peer Exchange
Current Topics:
- Languages on the JVM: Scala, Groovy, Clojure
- Enterprise Java
- Core Java, Java 7
- Agility
- Testing: Geb, Spock, Easyb
- REST
- NoSQL: MongoDB, Cassandra
- Hadoop
- Spring 3
- Automation Tools: Git, Hudson, Sonar
- HTML5, Ajax, jQuery, Usability
- Mobile Applications - iPhone and Android
- More...
NFJS, the Magazine
May Issue Now AvailableClient-Side MVC with Spine.js, Part 1
by Craig WallsOn Prototypal Inheritance, Part 2
by Raju GandhiMaking use of Scala Lazy Collections
by Venkat SubramaniamIntegration Testing Web Applications Using Gradle
by Kenneth Kousen


