Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to negate an ArgumentMatcher in Mockito?

I have created a ArgumentMatcher,

private class IsListOf2Elements implements ArgumentMatcher<List<String>>{
    @Override
    public boolean matches(List<String> argument) {
        return ((List<String>)argument).size()==2;
    }
}

I want to negate this match, so match when size is not 2,

Mockito.doReturn(false).when(mock).addAll(Mockito.argThat(AdditionalMatchers.not(new IsListOf2Elements())));

But this is not correct. I get,

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
No matchers found for additional matcher Not(?)
-> at my.test.own.Mockito_aTest.test4e(Mockito_aTest.java:136)
like image 687
ericj Avatar asked Oct 24 '16 10:10

ericj


People also ask

How do you use argument matchers in Mockito?

Mockito uses equal() as a legacy method for verification and matching of argument values. In some cases, we need more flexibility during the verification of argument values, so we should use argument matchers instead of equal() method. The ArgumentMatchers class is available in org. mockito package.

What can I use instead of Mockito matchers?

mockito. Matchers is deprecated, ArgumentMatchers should be used instead.

What is argThat in Mockito?

The argThat argument matcher in Mockito lets you create advanced argument matchers that run a function on passed arguments, and checks if the function returns true . If you have a complicated class that can't be easily checked using . equals() , a custom matcher can be a useful tool.

How do Mockito matchers work?

capture() it stores a matcher that saves its argument instead for later inspection. Matchers return dummy values such as zero, empty collections, or null . Mockito tries to return a safe, appropriate dummy value, like 0 for anyInt() or any(Integer. class) or an empty List<String> for anyListOf(String.


2 Answers

For reference, be aware that Hamcrest matchers and Mockito matchers behave very differently. Hamcrest's stateless Matcher objects represent the match function as an instance, and can be wrapped to invert their results; Mockito's "registered matchers" work only via side-effects.

argThat adapts a Hamcrest matcher into a Mockito matcher, Hamcrest's CoreMatchers.not inverts a Hamcrest matcher, and Mockito's AdditionalMatchers.not inverts a Mockito matcher (via side-effects). This is part of the reason that CoreMatchers.not always returns a Matcher<T>, while AddionalMatchers.not returns an arbitrary T; it's operating on Mockito state you can't see.

This gives you the following behavior:

// BAD: Don't use AdditionalMatchers to invert a Hamcrest matcher.
doReturn(false).when(mock).addAll(
    Mockito.argThat(AdditionalMatchers.not(new IsListOf2Elements())));

// GOOD: Use AdditionalMatchers to invert a Mockito matcher. (See ericj's answer.)
doReturn(false).when(mock).addAll(
    AdditionalMatchers​.not(Mockito.argThat‌​(new IsListOf2Elements())));

// ALSO GOOD: Use CoreMatchers to invert a Hamcrest matcher. (See troig's answer.)
doReturn(false).when(mock).addAll(
    Mockito.argThat‌​(CoreMatchers.not(new IsListOf2Elements())));

If the exception you're making is about stubbed behavior, you can also use a more-specific override to stub the general behavior in addition to the specific exception.

like image 192
Jeff Bowman Avatar answered Oct 26 '22 11:10

Jeff Bowman


It seems to be that you cannot use a custom matcher as AdditionalMatchers.not method parameter.

However, you can use hamcrest org.hamcrest.CoreMatchers instead. This should work:

Mockito.doReturn(false).when(mock).addAll(Mockito.argThat(CoreMatchers.not(new IsListOf2Elements())));

Hope it helps

like image 32
troig Avatar answered Oct 26 '22 11:10

troig