I’m using Mockito 1.10.18. I’m trying to create a custom argument matcher for a method that has the following signature …
SaveResult[] com.sforce.soap.enterprise.EnterpriseConnection.update(SObject[] sObjects) throws ConnectionException
I have written the following custom argument matcher …
class AccountMatcher extends ArgumentMatcher<SObject[]>
{
private Set<String> idList = new HashSet<String>();
AccountMatcher(final Set<Account> mainList)
{
for (final Account acct : mainList)
{
idList.add(acct.getId());
} // for
}
public boolean matches(Object param)
{
final SObject[] compSet = ((SObject[]) param);
final Set<String> compIdList = new HashSet<String>();
for (final SObject acct : compSet)
{
compIdList.add(acct.getId());
} // for
return Util.twoSetsMatch(compIdList, idList);
} // matches
}
However, when I try and set this up in my JUnit test …
final Set<Account> firstBatch = new HashSet<Account>();
firstBatch.add(acct);
final SObject[] firstBatchAccts = Matchers.argThat(new AccountMatcher(firstBatch));
Mockito.verify(mockConnection, Mockito.times(1)).update(firstBatchAccts);
I get the following exception when I run it …
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced argument matcher detected here:
-> at org.mainco.subco.sf.repo.AccountDaoIT.testUpdateAccountMaxRowsReached(AccountDaoIT.java:129)
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))
Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.
at org.mainco.subco.sf.repo.AccountDaoIT.testUpdateAccountMaxRowsReached(AccountDaoIT.java:130)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
This is because you've extracted firstBatchAccts
(the result of a Matcher call) to a variable, not a method. argThat
and all other Mockito matchers work via side-effects, so they have to be called at just the right time.
verify
checks for an empty stack of matchers, then resets expectations on the mock so they don't interfere with verification. Some matcher calls happen, and then the call to the method to be verified completes the process: It clears the stack, checks the method call happened, and switches the mock back to your stubbed behavior (if any).
The way you have it, argThat
gets called, and then the call to verify
happens, and verify
checks that no unused Matcher calls have happened. This triggers your exception.
Instead, inline the call:
verify(mockConnection).update(argThat(new AccountMatcher(firstBatch)));
...or make it a method call instead, which keeps the order right:
public SObject[] arrayMatchingAccounts(Set<Account> accountSet) {
return argThat(new AccountMatcher(accountSet));
}
verify(mockConnection).update(arrayMatchingAccounts(firstBatch));