问题
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:
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?
回答1:
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
来源:https://stackoverflow.com/questions/24467675/mockito-throwing-unfinishedverificationexception-probably-related-to-native-met