Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito: How to match a String and List<String> arguments

I'm basically trying to verify whether the correct parameters are being invoked in a method.

Here's a snippet of the code I'm trying to test:

Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.in("type", Arrays.asList("employee", "supervisor");

Verifying this using:

Mockito.verify(mockSession).createCriteria(User.class);
Mockito.verify(mockCriteria).add(Restrictions.in("type", Arrays.asList("employee", "supervisor"));

The first verify statement works. The second doesn't because, I believe, that the JVM detects two different List objects being compared to. However, when I change the second verify statement to:

Mockito.verify(mockCriteria).add(Restrictions.in("type", Mockito.anyList());

It works like a charm. However, I do want to ensure that the two Strings, employee and supervisor, are inside the List and that won't happen by using Mockito.anyList().

How do I get this to work?

EDIT: Please take note that I do not wish to only verify whether a List has been passed. I want to ensure that the correct Strings are passed inside that List as well

like image 292
mpmp Avatar asked Sep 27 '22 17:09

mpmp


1 Answers

Unfortunately, here you cannot easily check what you want to check with matchers.

Mockito matchers work via side-effects, where a call to a matcher tells Mockito to use the matcher rather than testing equality. That means that Mockito matchers don't work at all when nested within objects like Criterion.

verify(mockCriteria).add(
     Restrictions.in("type", Arrays.asList("employee", "supervisor"));

In the above, you don't use matchers, you're verifying that mockCriteria.add is called with an object that equals the Criterion you specify. However, if the returned Criterion doesn't override equals (and hashCode) then it will only test that the instances are the same—which will never be true here, because you're creating a new one in the verify statement.

verify(mockCriteria).add(Restrictions.in("type", anyList()));

Here, it looks like you're verifying that mockCriteria.add is called with any list, but anyList() actually tells Mockito to skip checking for one parameter and returns the dummy value null. You then create a Criterion where "type" in null, and then Mockito sees one any matcher on the stack for a one-argument method call, discards the newly-created invalid Criterion, and just checks that add was called at all. It looks like everything's working, but your mockCriteria could receive literally any parameter including null and the test would still pass. (If you were using a second matcher, or if add took two parameters, you would get InvalidUseOfMatchersException instead of your false positive.)

To make this work with Mockito matchers, you would need to write your own Hamcrest matcher that matches the entire Criterion, and then use argThat to let Mockito match arguments with it.


As Andy Turner mentioned, one way to solve this is to use ArgumentCaptor:

ArgumentCaptor<Criterion> captor = ArgumentCaptor.forClass(Criterion.class);
verify(mockCriteria).add(captor.capture());
Criterion criterion = captor.getValue();
// assert against criterion

However, be warned that this may be of limited use: Criterion doesn't have a lot of properties you can inspect. You may need to use toString(), as in this SO question.

like image 185
Jeff Bowman Avatar answered Nov 02 '22 12:11

Jeff Bowman