Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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("[email protected]");
        assertThat(firstInvocation, is(MEMBER_ONE));

        Member secondInvocation = memberRepositoryMock.findByEmail("[email protected]");
        assertThat(secondInvocation, is(MEMBER_ONE));

        verify(memberRepositoryMock, times(1)).findByEmail("[email protected]");

        Member thirdInvocation = memberRepositoryMock.findByEmail("[email protected]");
        assertThat(thirdInvocation, is(MEMBER_TWO));

        verify(memberRepositoryMock, times(1)).findByEmail("[email protected]");
    }

    @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:

  • I am not sure what gets invoked here: sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)... It has to do with reflection and a native method but which native method in my case?
  • What do I get wrong with my Mockito usage?

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("[email protected]");

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?

like image 843
balteo Avatar asked Jun 28 '14 14:06

balteo


1 Answers

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("[email protected]");
        assertThat(firstInvocation, is("foo"));

        String secondInvocation = cachedRepository.findByEmail("[email protected]");
        assertThat(secondInvocation, is("foo"));

        verify(mockProvider.get(), times(1)).findByEmail("[email protected]");

        String thirdInvocation = cachedRepository.findByEmail("[email protected]");
        assertThat(thirdInvocation, is("bar"));

        verify(mockProvider.get(), times(1)).findByEmail("[email protected]");
    }

    @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

like image 66
macias Avatar answered Oct 13 '22 21:10

macias