0

Using Mockito, I need to test a method that gets called twice in the same transaction and returns different values each time.

I have two tests - one for each call.

The first is:

verify(mockAppender, times(2)).doAppend(logEventArgumentCaptor.capture());

assertThat(logEventArgumentCaptor.getValue().getMessage(), containsString("Error response: " + RESPONSE ));

whilst the 2nd reads:

verify(mockAppender, times(2)).doAppend(logEventArgumentCaptor.capture());

assertThat(logEventArgumentCaptor.getValue().getMessage(), containsString("Request body: " + BODY));

The code being tested reads:

if (!statusCode.is2xxSuccessful()) {
  LOG.error("Error response: "+response);
  LOG.error("Request body: "+bodyString);
}

The 2nd test passes because it finds the 2nd response, but the 1st fails because it also finds the 2nd response. How can I get the first test to look only at the first response from the method? Or is there a form of assertThat that would allow me to check both responses at once?

Patrick
  • 77
  • 1
  • 3
  • 9

1 Answers1

0

ArgumentCaptor supports a getAllValues() call that returns a list of every matching capture. You could use this to verify both messages.

verify(mockAppender, times(2)).doAppend(logEventArgumentCaptor.capture());

assertThat(logEventArgumentCaptor.getAllValues().get(0).getMessage(),
    containsString("Error response: " + RESPONSE ));
assertThat(logEventArgumentCaptor.getAllValues().get(1).getMessage(),
    containsString("Request body: " + BODY));

However, in the general case, this is a perfect opportunity to write a Hamcrest(-style) matcher that checks the message for you. I use Mockito's ArgumentMatcher class here, which extended Hamcrest's Matcher in Mockito 1.x but breaks that dependency in Mockito 2.0.

public class LogEventContaining implements ArgumentMatcher<LogEvent> {
  private final String searchText;

  public LogEventContaining(String searchText) {
    this.searchText = searchText;
  }

  @Override public boolean matches(LogEvent logEvent) {
    // You could also use your Hamcrest matcher here.
    return logEvent.getMessage().indexOf(searchText) >= 0;
  }

  @Override public String toString() {
    return String.format("log message containing '%s'", searchText);
  }
}

// Per Mockito/Hamcrest style. This could go anywhere.
public static ArgumentMatcher<LogEvent> isALogEventContaining(String searchText) {
  return new LogEventContaining(searchText);
}

Now you can use it in both when and verify calls, wrapped with adapter argThat:

verify(mockAppender)
    .doAppend(argThat(isALogEventContaining("Error response: " + RESPONSE)));
verify(mockAppender, never())
    .doAppend(argThat(isALogEventContaining("Request body: " + BODY)));
when(mockAppender.doAppend(argThat(isALogEventContaining("Foo"))))
    .thenThrow(new RuntimeException("Log not available"));

For more about which argThat to use and why you need it, see: How do Mockito matchers work?

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Great answer. It's alway a thrill to see that line go green. Thanks. – Patrick Jul 16 '16 at 16:53
  • @Patrick Glad to help! When you feel like this question has been answered to your satisfaction, you can [accept an answer](http://stackoverflow.com/help/accepted-answer) by clicking the checkbox. Welcome to StackOverflow, and good luck with your project! – Jeff Bowman Jul 16 '16 at 17:25