6

What I've got is this line:

verify( mockAppendable ).append( org.mockito.Mockito.contains( msg ) );

... but I want this test to be case-indifferent. How would I do that?

weston
  • 54,145
  • 21
  • 145
  • 203
mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • 2
    Using argThat, for example: https://static.javadoc.io/org.mockito/mockito-core/2.7.5/org/mockito/ArgumentMatchers.html#argThat(org.mockito.ArgumentMatcher) – JB Nizet Feb 12 '17 at 11:32
  • Thanks. I'm wondering whether it's possible to do it as a one-liner, with Java 8 lambda, as per this page to which your link directs: https://static.javadoc.io/org.mockito/mockito-core/2.7.5/org/mockito/ArgumentMatcher.html – mike rodent Feb 12 '17 at 11:45

4 Answers4

7

On the page, what do they mean by "you can extract method" and is it possible to use a Java 8 lambda to make this a one-liner?

In a single line using case insensitivity code from here:

verify(mockAppendable)
    .append(
       argThat(arg ->
         Pattern.compile(Pattern.quote(msg), Pattern.CASE_INSENSITIVE).matcher(arg).find()));

So they allowed the lambda version by skipping specifying a custom error message.

And by "you can extract a method", they mean:

verify(mockAppendable).append(argThat(containsCaseInsensitive(msg)));

And the method is defined as:

public static ArgumentMatcher<String> containsCaseInsensitive(final string s) {
    if (s == null) throw new IllegalArgumentException("s is null");
    final Pattern pattern = Pattern.compile(Pattern.quote(s), Pattern.CASE_INSENSITIVE);
    return new ArgumentMatcher<String>() {

        @Override
        public boolean matches(String arg) {
            return arg != null && pattern.matcher(arg).find();
        }

        @Override 
        public String toString() {
            return String.format("[should have contained, ignoring case, \"%s\"]", s);
        }
    };
}

That's completely reusable, you can put that inside a new class like MocitoMatchers for example and call from many tests just like any of the built-in matchers.

Community
  • 1
  • 1
weston
  • 54,145
  • 21
  • 145
  • 203
  • Brilliant. Thanks very much! – mike rodent Feb 12 '17 at 12:47
  • Would you care to have a look at my version of your "containsCaseInsensitive"? And possibly edit yours? (to take account of null arguments and slightly better readability) ... I tried submitting an edit but I messed up and the third-party reviewers rejected it... – mike rodent Feb 12 '17 at 20:39
  • Yours may be more complete, but SO isn't here to provide copy and pasteable, library quality code. All the null checks and different messages are just noise people can choose to add if they so wish and don't make a difference to the question. – weston Feb 12 '17 at 21:31
  • On second thoughts it probably would be better to return `false` in the event of a `null` `arg`ument and might as well check the input too. – weston Feb 13 '17 at 13:42
  • I've used `s` for the string because that's the name of the parameter in `String::contains`. The message is really a personal preference, but I'll use yours. – weston Feb 13 '17 at 13:44
0

Thanks to JB Nizet I've worked out that the answer appears to be a class like this:

class ContainsThisIgnoringCase implements ArgumentMatcher<String> {
    private String msg;
    ContainsThisIgnoringCase( String msg ){
        this.msg = msg;
    }
    @Override
    public boolean matches(String arg ) {
        return arg.toLowerCase().contains( msg.toLowerCase() );
    }
    @Override 
    public String toString(){
        return msg;
    }
}

However, I'm puzzled by the stuff I then see on the page to which his link directs:
a) what do they mean by "you can extract method" and
b) is it possible to use a Java 8 lambda to make this a one-liner?

mike rodent
  • 14,126
  • 11
  • 103
  • 157
0

On the page, what do they mean by "you can extract method" and is it possible to use a Java 8 lambda to make this a one-liner?

This just means that instead of having to put new ListOfTwoElements() in your test code, you could add a method to your test class called listOfTwoElements() that returns a new ListOfTwoElements object to make the test code more fluent.

To answer your question about making this a one-liner, and more importantly, to answer this posting's original question, you can use MockitoHamcrest from the core Mockito library and IsEqualIgnoringCase from the Hamcrest library to get a one-liner without needing a custom class or lambda:

import static org.hamcrest.text.IsEqualIgnoringCase.equalToIgnoringCase;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;

class CookieUtilTest {

    @Mock
    private HttpServletResponse response;

    @Test
    public void testHasCookiesDoesNotMatchCaseSensitiveCookieName() {
        when(response.getHeaders(argThat(equalToIgnoringCase("Set-Cookie")))).thenReturn(singletonList("CookieNames=AreCaseSensitive"));

        boolean testValue = CookieUtil.hasCookie("cookieNames", response);

        verify(response).getHeaders(argThat(equalToIgnoringCase("Set-Cookie")));
        assertThat(testValue).isFalse();
    }
}
chaserb
  • 1,340
  • 1
  • 14
  • 25
0

By far the easiest way without writing an extra matcher would be using the matches(String regex) helper. Just use the embedded case insensitive flag with your string and you are good to go:

verify( mockAppendable ).append( matches("(?i)the-message") );
Jacob van Lingen
  • 8,989
  • 7
  • 48
  • 78