Mockito throwing UnfinishedVerificationException (probably related to native method call)
Asked Answered
K

2

7

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://mcmap.net/q/742146/-how-to-test-spring-39-s-declarative-caching-support-on-spring-data-repositories 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?

Kofu answered 28/6, 2014 at 14:14 Comment(6)
Could you post the MemberRepository code as well?Preachment
@macias: I have posted the MemberRepository (a spring data jpa repository).Kofu
+1 interesting. Seems like spring wraps your mocked repository into some proxy and you don't receive the mock reference but the proxy one which cannot be stubbed.Preachment
Ummhh.. what about the sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) line? I am really puzzled about it... what is native here?Kofu
The sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) line is just the JUnit runner using reflection to find your test methods and run them. It is not an indication that you are using a proxy. However, it's not an indication that you aren't using a proxy either. As macias says, debug the test and find out what type it actually is.Mycology
Please find the update to my answer...Preachment
P
9

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

Preachment answered 28/6, 2014 at 16:59 Comment(6)
thanks. I have edited my post. One thing to take into account is that I am testing Spring cache and I therefore need a spring context. Any idea on how to test the cache without actually hitting the database?Kofu
The answer for this is here https://mcmap.net/q/752783/-issues-with-testing-spring-39-s-declarative-caching-support-on-a-spring-data-repository. Note that with the static reference trick @frant.hartm makes sure that you are invoking calls to cache and then verify how many times your actual mock was invoked.Preachment
I was also thinking about your 3rd edit, it's weird. It might be some lucky coincidence connected to the fact that the cache calls the actual mock down in the stack somewhere and the exception you get occurs only if you explicitely validateMockitoUsage. I'm not sure...Preachment
Hi Macias. Ummm. What do you mean by RepositoryDecorator? I was not able to find information about this...Kofu
Oh pardon, this was just some leftover from my testing (it was simulating the proxy). Corrected the answer.Preachment
I must be a moron - but this works and I really have no idea why? Can someone explain please?Overscore
I
0

I would like to post my approach here for future references.

The key point is that Bean is a proxy of your original object, so you shall not try to mock its behavior or apply any other Mockito operations.

Instead, you shall retrieve the object inside the Bean to set up its mocking behavior and verify its method invocations. And that is why AopTestUtils$getTargetObject exists.

You could retrieve the object with AopTestUtils$getTargetObject for applying mock and verify functions. Note that it is a generic method. You could pass in the type of your object inside the Bean.

For more details, please have a look at Spring testing document and its javadoc.

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

    @Autowired
    private MemberRepository cachedRepository;

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

    @Test
    public void test() {
                

        // Use `AopTestUtils$getTargetObject` to get target object inside the Bean.
        when(AopTestUtils.<MemberRepository>getTargetObject(cachedRepository)).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"));

        // Use `AopTestUtils$getTargetObject` to get target object inside the Bean.
        verify(AopTestUtils.<MemberRepository>getTargetObject(cachedRepository), times(1)).findByEmail("[email protected]");
    }

    @Configuration
    static class Config {
        @Bean
        public MemberRepository cachedRepository() {
            return mock(MemberRepository.class);
        }
    }
}

Inurn answered 27/8, 2022 at 2:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.