Mockito – ArgumentMatcher vs ArgumentCaptor

Since a few weeks the new mockito release candidate is available. So I think it is the right time to place a new post. Here I will show the differences between ArgumentMatcher and ArgumentCaptor. Most developer will first see the ArgumentMatcher and use it. What can be done with ArgumentCaptor is often not seen. With the sample I will show that an ArgumentMatcher should not be used to fetch arguments. For this work there is the ArgumentCaptor. The Matcher should really be used to check if an argument matches or not.

Both samples will do the same: Create a mocked Sample object and verify the Object passed to setOtherSample( ) contains the expected name. The code of the interface and implementing class you will find at the bottom.
Have you ever seen code like this:

@Test
public void doSomeChecksWithAnswer() {
  final Sample sample = mock(Sample.class);
  when(sample.setOtherSample(argThat(new ArgumentMatcher<Sample>() {
    @Override
    public boolean matches(final Object argument) {
      assertEquals("Wrong name", "name", ((Sample) argument).getName());
      return true;
    }
  }))).thenReturn(Boolean.TRUE);
  final Sample other = new SampleImpl();
  other.setName("otherName");
  sample.setOtherSample(other);
}

Sure it works. But it looks very unhandy nearly encroached. This is because the ArgumentMatcher in the when is designed to be used to give the right answer by different arguments.
For validation it is more practicable to use ArgumentCaptor. If you read the following code you will agree it looks simpler than the version with the matcher.

@Test
public void doSomeChecksWithCaptor() {
  final Sample sample = mock(Sample.class);
  when(sample.getName()).thenReturn("myName");
  final Sample other = new SampleImpl();
  other.setName("otherName");
  sample.setOtherSample(other);
  final ArgumentCaptor<Sample> argumentCaptor = 
                      ArgumentCaptor.forClass(Sample.class);
  verify(sample).setOtherSample(argumentCaptor.capture());
  assertEquals("Wrong name", "name", argumentCaptor.getValue().getName());
}

With the captor it is possible to get the whole object passed to the method and do the assertions. By comparing the result of the two tests you will recognize the version with the match will fail on the call to setOtherSample and the captor version fails as expected on the assertEquals line.
If you read the samples you will have seen, the test will fail. This is for the case you like to try this in your own workspace. It would be much more meaningful as a green running test.
I hope the sample could tell you when to use a matcher and when to use a captor.

public interface Sample {
  void setName(String nama);
  String getName();
  void setOtherSample(Sample sample);
}

public class SampleImpl implements Sample {
  private String name;
  @Override
  public String getName() {
    return name;
  }

  @Override
  public void setName(final String name) {
    this.name = name;
  }

  @Override
  public void setOtherSample(final Sample sample) {
  }
}

About the author

Adrian Elsener

2 comments

  • Would you mind doing another evaluation on this now given that the new mockito API leveraged java 8 lambda to made ArgumentMatcher much less of a pain?

By Adrian Elsener

Recent Posts