GuyKhmel
GuyKhmel

Reputation: 505

Mockito - Is there a matcher for "value not in List"?

I currently have a mock which has specific behaviors for specific set of inputs. Every other input should return a specific response.

For example:

    Mockito.when(classInstance.myFunc(Mockito.eq("Option1"))).thenReturn(answer1);
    Mockito.when(classInstance.myFunc(Mockito.eq("Option2"))).thenReturn(answer2);
    Mockito.when(classInstance.myFunc(Mockito.eq("Option3"))).thenReturn(answer3);
    Mockito.when(classInstance.myFunc(Mockito.eq("Option4"))).thenReturn(answer4);

    // Return defaultAnswer if and(and(not("Option1"), not("Option2")), and(not("Option3"), not("Option4")))
    Mockito.when(classInstance.myFunc(AdditionalMatchers.and(AdditionalMatchers.and(AdditionalMatchers.not(Mockito.eq("Option1")), AdditionalMatchers.not(Mockito.eq("Option2")), AdditionalMatchers.and(AdditionalMatchers.not(Mockito.eq("Option3")), AdditionalMatchers.not(Mockito.eq("Option4")))).thenReturn(defaultAnswer);

My biggest trouble is the complexity of the and(and(not("Option1"), not("Option2")), and(not("Option3"), not("Option4"))) line.

I really hope that there is an easier way to specify a condition of "everything else" or just "not in list:["option1", ...] "

Is there a matcher for "Within a group" or something similar?

Upvotes: 4

Views: 2621

Answers (3)

davidxxx
davidxxx

Reputation: 131456

Why not simply use Mockito.matches(boolean) such as :

 import static org.mockito.Mockito.*;

 Mockito.when(classInstance.myFunc(matches("Option[^1-4]"))
        .thenReturn(defaultAnswer);

As alternative you can also use Mockito.argThat().
To filter out some integer values (as suggested in your comment) you could write :

 import static org.mockito.Mockito.*;

 List<Integer> toNotMatchList = Arrays.asList(1, 2, 3, 4) ;
 Mockito.when(classInstance.myFunc(argThat(i -> !toNotMatchList.contains(i))
        .thenReturn(defaultAnswer);

or more straightly :

Mockito.when(classInstance.myFunc(argThat(i -> !Arrays.asList(1, 2, 3, 4).contains(i))
        .thenReturn(defaultAnswer);

Upvotes: 5

ryanp
ryanp

Reputation: 5127

You could probably make it a bit more readable by defining the default explicitly, then 'overriding' this with subsequent stubbings:

when(classInstance.myFunc(any()).thenReturn(defaultAnswer);
when(classInstance.myFunc("Option1").thenReturn(answer1);
when(classInstance.myFunc("Option2").thenReturn(answer2);
...

Or you could use MockitoHamcrest and Hamcrest's core matchers to simplify it a bit, e.g.:

when(classInstance.myFunc(argThat(is(allOf(not("Option1"), not("Option2"), ...))))
    .thenReturn(...)

Or you could use MockitoHamcrest and your own Hamcrest matcher.

Upvotes: 5

GhostCat
GhostCat

Reputation: 140523

I currently have a mock which has specific behaviors for specific set of inputs. Every other input should return a specific response.

Is like, wrong.

The purpose of unit tests is to get you quickly to a bug. In order to understand what is going on, you want unit tests to be as much "self contained" as possible. You read the test, maybe the setup method, and you understand what is going on (or at least: going "into" your code under test).

Having multiple specs to cover multiple cases doesn't help with that, to the contrary. You don't want that.

Instead, you want as few specifications as possible. If your mock sees different input, and should return different results, then that should be something you do per test case. Not "in general", in your setup method.

So the distinct non-answer: avoid having multiple specs like that.

Upvotes: 3

Related Questions