javaspringreflectionmockitonative-methods

Mockito throwing UnfinishedVerificationException (probably related to native method call)


I have the following exception when I run a test case:

org.mockito.exceptions.misusing.UnfinishedVerificationException: 
Missing method call for verify(mock) here:
-> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

Example of correct verification:
    verify(mock).doSomething()

Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.

    at com.bignibouX.tests.repository.member.MemberCachingIntegrationTest.testFindByEmail(MemberCachingIntegrationTest.java:61)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:72)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:81)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:215)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:81)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:60)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:67)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:161)
    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)

The test case has two tests which are as follows:

@ActiveProfiles(Profiles.TEST)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { FullIntegrationTestConfiguration.class, BaseTestConfiguration.class, MemberCachingIntegrationTest.Config.class })
public class MemberCachingIntegrationTest {

    private static final Member MEMBER_ONE = new Member();
    private static final Member MEMBER_TWO = new Member();

    @Autowired
    private MemberRepository memberRepositoryMock;

    @After
    public void validate() {
        validateMockitoUsage();
    }

    @Test
    public void testFindByEmail() {
        when(memberRepositoryMock.findByEmail(anyString())).thenReturn(MEMBER_ONE, MEMBER_TWO);

        Member firstInvocation = memberRepositoryMock.findByEmail("foo@foo.com");
        assertThat(firstInvocation, is(MEMBER_ONE));

        Member secondInvocation = memberRepositoryMock.findByEmail("foo@foo.com");
        assertThat(secondInvocation, is(MEMBER_ONE));

        verify(memberRepositoryMock, times(1)).findByEmail("foo@foo.com");

        Member thirdInvocation = memberRepositoryMock.findByEmail("bar@bar.com");
        assertThat(thirdInvocation, is(MEMBER_TWO));

        verify(memberRepositoryMock, times(1)).findByEmail("bar@bar.com");
    }

    @Test
    public void passingInexistentEmailToSendPasswordResetShouldNotCauseNPE() {
        fail("MemberRepository's findByEmail throws NPE if email not found in db! Reason: because cache was set up not to allow null values...");
        fail("Appropriate error page not displayed when above NPE is thrown!");
    }

    @Profile(Profiles.TEST)
    @Configuration
    static class Config {

        @Bean
        public MemberRepository memberRepositoryMock() {
            return mock(MemberRepository.class);
        }
    }
}

My question is twofold:

edit:

@RooJpaRepository(domainType = Member.class)
public interface MemberRepository {

    @Cacheable(value = CacheConfiguration.DATABASE_CACHE_NAME)
    Member findByEmail(String email);

    @Cacheable(CacheConfiguration.DATABASE_CACHE_NAME)
    Member findByToken(String token);

    @CacheEvict(value = CacheConfiguration.DATABASE_CACHE_NAME, key = "#result.email")
    <S extends Member> S save(S entity);
}

edit 2:

I have debugged the test and included the screen capture which seems to bear out what macias said:

output from debug

org.springframework.aop.framework.ProxyFactory: 2 interfaces [com.bignibou.repository.member.MemberRepository, org.mockito.cglib.proxy.Factory]; 1 advisors [org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor: advice bean 'null']; targetSource [SingletonTargetSource for target object [com.bignibou.repository.member.MemberRepository$$EnhancerByMockitoWithCGLIB$$1b543cbe@4cca9ed4]]; proxyTargetClass=false; optimize=false; opaque=false; exposeProxy=false; frozen=false

edit 3: One other thing to consider: if I remove or comment out both the last line in the test and the tear-down method i.e.

verify(memberRepositoryMock, times(1)).findByEmail("bar@bar.com");

and

@After
public void validate() {
        validateMockitoUsage();
}

The test passes without problem....

edit 4: I am actually desperately trying to adapt the following example: https://stackoverflow.com/a/24229350/536299 that was given to me in another post. One does notice that the mock is autowired and the test does use a spring context. Can someone please help me get my test right?


Solution

  • The sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - according to the stack trace you provided, mockito got a bit confused when reporting the error. It's not really related with your problem, but anyway that's the NativeMethodAccessorImpl.invoke0 which is native (see grep code). The SpringJUnit4ClassRunner runs it, it's how it works, nothing you should care about.

    Now about your real problem, as I wrote in comment, It's because Spring wraps the mock object into a proxy. If you add

    System.out.println(memberRepositoryMock.getClass());
    

    you'll see in the console that it's not the mock but some proxy there. Mockito can only stub mocks, so that's why you recieve the error.

    The question might be now, how to work this around. First thing is that your test is not actually an integration test as you are trying to mock the repository instead of really test how the data access behave. In this case I would resign from using the spring-test and simply go for simple MockitoJUnitRunner.

    UPDATE:

    OK, now knowing that it is actually the cache to be tested, I understand you need the mocked repository to be decorated with Spring cache and that your test IS an integration test indeed.

    Here's how it could be done. A little variation of mentioned frant.hartm proposal but without use of static reference. Slightly more natural I think.

    @ActiveProfiles(Profiles.TEST)
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = { FullIntegrationTestConfiguration.class, BaseTestConfiguration.class, MemberCachingIntegrationTest.Config.class })
    public class MemberCachingIntegrationTest {
    
        @Autowired
        private MemberRepository cachedRepository;
    
        @Autowired
        private MockProvider mockProvider;
    
        @After
        public void validate() {
            validateMockitoUsage();
        }
    
        @Test
        public void test() {
            when(mockProvider.get().findByEmail(anyString())).thenReturn("foo", "bar");
    
            String firstInvocation = cachedRepository.findByEmail("foo@foo.com");
            assertThat(firstInvocation, is("foo"));
    
            String secondInvocation = cachedRepository.findByEmail("foo@foo.com");
            assertThat(secondInvocation, is("foo"));
    
            verify(mockProvider.get(), times(1)).findByEmail("foo@foo.com");
    
            String thirdInvocation = cachedRepository.findByEmail("bar@bar.com");
            assertThat(thirdInvocation, is("bar"));
    
            verify(mockProvider.get(), times(1)).findByEmail("bar@bar.com");
        }
    
        @Configuration
        static class Config {
    
            private MemberRepository mockRepository = mock(MemberRepository.class);
    
            @Bean
            public MemberRepository cachedRepository() {
                return mockRepository;
            }
    
            @Bean
            public MockProvider mockProvider() {
                return new MockProvider(mockRepository);
            }
    
        }
    
        public static class MockProvider {
    
            private final MemberRepository repository;
    
            public MockProvider(MemberRepository repository) {
                this.repository = repository;
            }
    
            public MemberRepository get() {
                return this.repository;
            }
    
        }
    }
    

    Note: Repository returns Strings instead of Members for a more clear example