Recent site activity

Argument matching with Mockito

I recently fielded a question at a client about how to state verify indirect inputs into a mocked dependency.  It caught me off guard when the question was asked (the question came first thing in the morning on an iteration planning day), so I didn't just rattle off an answer.  Well, that question gnawed at me all day and so I thought I'd tried to figure out the answer.  First, let me set the context.  Our SUT for this example in the SendMessagesCommand.  This command can send one or multiple text messages to a destination.  The command collaborates with a MessageSender (a dependency injected into the SendMessagesCommand); the MessageSender will be mocked by Mockito.  The two types are shown below:

SendMessagesCommand class:

 1 package examples.mockito;
2
3
import java.text.MessageFormat;
4 import java.util.ArrayList;
5 import java.util.List;
6 import java.util.Map;
7
8
/**
9 * @author Christopher Bartling, Pintail Consulting LLC
10 * @since Oct 8, 2008
11 */
12 public class SendMessagesCommand {
13
14
private MessageSender messageSender;
15 public static final String CONTEXT_KEY_NAMES = "names";
16
17
public void setMessageSender(MessageSender messageSender) {
18 this.messageSender = messageSender;
19 }
20
21
public void execute(Map context) {
22 String message;
23 if (context.containsKey(CONTEXT_KEY_NAMES)) {
24 final String[] names = (String[]) context.get(CONTEXT_KEY_NAMES);
25 List<String> messagesList = new ArrayList<String>();
26 for (String name : names) {
27 message = MessageFormat.format("<message><name>{0}</name></message>", name);
28 messagesList.add(message);
29 }
30 messageSender.send(messagesList);
31 }
32 }
33 }
34
The SendMessagesCommand creates the formatted messages and then sends them along to the MessageSender.  We want to intercept the formatted messages as they are passed to the test double dependency.  I want to state verify this list of formatted messages.

MessageSender interface:

 1 package examples.mockito;
2
3
import java.util.List;
4
5
/**
6 * @author Christopher Bartling, Pintail Consulting LLC
7 * @since Oct 8, 2008
8 */
9 public interface MessageSender {
10
11
void send(List<String> messages);
12 }
13

As you can see, we can easily verify behavior of the SendMessagesCommand (i.e. we can verify that SendMessagesCommand eventually calls MessageSender.send() method).  It becomes a bit more difficult to do the state verification, because we don't have access to the indirect inputs to the test double dependency.  This situation is easily remedied by using Matchers and specifically extending the org.mockito.ArgumentMatcher.  The test case below verifies both the behavior and the state of the collaboration between the SUT and the dependency.  All the state verification occurs in the MessagesArgumentMatcher.matches method.

SendMessagesCommandTests class:

 1 package examples.mockito;
2
3
import org.junit.Before;
4 import org.junit.Test;
5 import org.mockito.ArgumentMatcher;
6 import static org.mockito.Matchers.argThat;
7 import static org.mockito.Mockito.verify;
8 import org.mockito.MockitoAnnotations;
9
10
import java.util.HashMap;
11 import java.util.List;
12 import java.util.Map;
13
14
/**
15 * @author Christopher Bartling, Pintail Consulting LLC
16 * @since Oct 8, 2008
17 */
18 public class SendMessagesCommandTests {
19
20
SendMessagesCommand command;
21
22
@MockitoAnnotations.Mock
23 private MessageSender messageSender;
24
25
@Before
26 public void doBeforeEachTestCase() {
27 MockitoAnnotations.initMocks(this);
28 command = new SendMessagesCommand();
29 command.setMessageSender(messageSender);
30 }
31
32
@Test
33 public void executeMultipleMessagesSuccessfully() {
34 Map context = new HashMap();
35 context.put(SendMessagesCommand.CONTEXT_KEY_NAMES, new String[]{"Joe Smith", "John Doe", "Eddie Moola"});
36 command.execute(context);
37
38
// Verify indirect input to the MessageSender.
39 verify(messageSender).send((List<String>) argThat(new MessagesArgumentMatcher()));
40 }
41
42
class MessagesArgumentMatcher extends ArgumentMatcher {
43
44
public boolean matches(Object o) {
45 if (o instanceof List) {
46 List<String> strings = (List<String>) o;
47 if (strings.size() != 3) return false;
48 if (!strings.get(0).equals("<message><name>Joe Smith</name></message>")) return false;
49 if (!strings.get(1).equals("<message><name>John Doe</name></message>")) return false;
50 if (!strings.get(2).equals("<message><name>Eddie Moola</name></message>")) return false;
51 }
52 return true;
53 }
54 }
55 }
56
Line 39 verifies that the send method of the MessageSender was invoked.  In addition, we further verify the first and only argument passed to that method by passing in an instance of the MessagesArgumentMatcher.  The argThat matcher helps hook up my custom matcher implementation.   Lines 42-54 are my custom argument matcher implementation.

Comments