Issue #36 resolved

Matchers don't work but mocks do

Pankaj Tandon
created an issue

I'm trying to use this with Spring MVC testing:

I used springockito to create my mocks using xml config in a file called mock-services-context.xml like so:

<mockito:mock id="userService" class="com.xxx.services.user.UserServiceImpl" />

Then I configured my tests like so:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations={"/META-INF/spring/api-bootstrap-context.xml", "classpath:mock-services-context.xml"})
public class AuthenticationControllerTests {
   ...
}

In my setup, I extracted the user bean to try to mock it.

    @Before
    public void setup() {
        //Build  the MVC context
        this.mockMvc = webAppContextSetup(this.wac).build();        
        user = new User();
        user.setId(1L);
        user.setUserName("aUserName");
        userService = wac.getBean("userService", UserService.class);
        when(userService.findUserByPlainTextCredentials(anyString(), anyString())).thenReturn(user);
    }

That last line is where it blows up. The matchers don't seem to work with the mocked object.

But when I replace the expectation like so:

        when(userService.findUserByPlainTextCredentials("joe", "secret")).thenReturn(user);

all works fine.

I did confirm that userService is a mocked CGLIB proxy.

Here's the stack:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced argument matcher detected here:
-> at com.xxx.alps.api.HomeControllerTests.setup(HomeControllerTests.java:52)

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().

    at com.xxx.alps.api.HomeControllerTests.setup(HomeControllerTests.java:52)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    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:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    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:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Any idea why the matchers don't work?

Thanks for the great work, tho' Just what we need at this point!

Comments (8)

  1. Philip Maes

    The problem is in ThreadLocalMockMethodInterceptor<T> which adds a wrap around the mock.

    Then, when Mockito.verify or Mockito.when(mock.method(matcheris)) are called, there is a test in Mockito to check if the given mock is a MockitoMock. Conditions to be a true MockitoMock are to be !=null and that the callback of the mock is instanceof MethodInterceptorFilter, which is not anymore the case, so the mock is not recognized as a Mockito mock. (CglibMockMaker, method getHandler(Object mock) line ~39)

    I don't know why this is needed because the one of Mockito is already fine for that purpose.

    So the solution is simply to fork this repo and delete the class ThreadLocalMockMethodInterceptor.

  2. kubek2k repo owner

    I think the problem is solved in 1.0.7 - I resigned from thread locals - the idea was to make mocks be able to work in a multithreaded environment, but it feels like its not the time yet :|. Please recheck

  3. Log in to comment