Mockito ArgumentMatchers doesNotMatch?

1.4k views Asked by At

ArgumentMatchers.matches( String regex ) exists... and it is possible to devise regexes which don't match a given String. But it is far from trivial (several threads in SO).

Is it wrong of me (or wrong-headed) to think it might be a nice idea to request the Mockito designers to take the heavy-lifting out of this and add it as a feature? It just seems that, in the context of mocking and so forth, it is a far-from-exceptional use case...

PS also, I'm not clear with ArgumentMatchers.matches how you go about saying "this may be a multiline String we're matching against, don't worry about it"... wouldn't it be better to have a Pattern rather than a simple String?

later

Feature request "enhanced" at Mockito HQ (on Github). "bric3" there says one should use Jeff Bowman's technique for "does not match". But she/he seems to think the Pattern idea is worth thinking about.

Re not(): Mockito's own documentation says "Use additional matchers very judiciously because they may impact readability of a test. It is recommended to use matchers from Matchers and keep stubbing and verification simple."

Also I find I must "possible dupe" my own question: How to write a matcher that is not equal to something. Searching with hindsight is always easier...!

later still

Many thanks to Brice for adding this so quickly. Updated my gradle.build and... new 4.1 core downloaded from Maven Central and immediately available for use.

2

There are 2 answers

1
Jeff Bowman On BEST ANSWER

No need for a request: You can compose what you want using AdditionalMatchers.not.

when(yourComponent.acceptString(not(matches("foo|ba[rz]"))))
    .thenThrow(new IllegalArgumentException());

If you want to match a Pattern, you might need to write your own ArgumentMatcher subclass, but it's quite easy from there:

public class MatchesPattern implements ArgumentMatcher<String> {
  private final Pattern pattern;
  public MatchesPattern(Pattern pattern) { this.pattern = pattern; }

  @Override public boolean matches(String string) {
    return pattern.matcher(string).matches();
  }

  @Override public String toString() {
    return "[string matching /" + pattern.toString() + "/]";
  }

  /** Optional. */
  public static MatchesPattern matchesPattern(Pattern pattern) {
    return new MatchesPattern(pattern);
  }
}

You can then consume that class using:

when(yourComponent.acceptString(not(argThat(new MatchesPattern(yourPattern)))
    .thenThrow(new IllegalArgumentException());

// or with the static factory method:
when(yourComponent.acceptString(not(argThat(matchesPattern(yourPattern)))
    .thenThrow(new IllegalArgumentException());
2
bric3 On

For future readers, Mockito 2.4.1 has been released with support of the Pattern class :

Now you should be abble to write :

when(yourComponent.acceptString(not(matches(Pattern.compile(...)))
    .thenThrow(new IllegalArgumentException());