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("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:

  • 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("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?

Community
  • 1
  • 1
balteo
  • 23,602
  • 63
  • 219
  • 412
  • Could you post the MemberRepository code as well? – makasprzak Jun 28 '14 at 15:46
  • @macias: I have posted the MemberRepository (a spring data jpa repository). – balteo Jun 28 '14 at 15:52
  • 1
    +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. – makasprzak Jun 28 '14 at 16:05
  • Ummhh.. what about the `sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)` line? I am really puzzled about it... what is native here? – balteo Jun 28 '14 at 16:09
  • 1
    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. – Luke Woodward Jun 28 '14 at 16:57
  • Please find the update to my answer... – makasprzak Jul 02 '14 at 17:16

2 Answers2

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("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

Community
  • 1
  • 1
makasprzak
  • 5,082
  • 3
  • 30
  • 49
  • 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? – balteo Jun 28 '14 at 20:13
  • The answer for this is here http://stackoverflow.com/a/24336638/1937263. 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. – makasprzak Jun 29 '14 at 03:30
  • 1
    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... – makasprzak Jun 29 '14 at 04:00
  • Hi Macias. Ummm. What do you mean by `RepositoryDecorator`? I was not able to find information about this... – balteo Jul 04 '14 at 18:55
  • Oh pardon, this was just some leftover from my testing (it was simulating the proxy). Corrected the answer. – makasprzak Jul 04 '14 at 19:26
  • 1
    I must be a moron - but this works and I really have no idea why? Can someone explain please? – kellyfj May 17 '17 at 19:22
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("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"));

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

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

jadore801120
  • 77
  • 1
  • 3