2014-06-16 13 views
3

我想爲Spring Data JPA存儲庫(擴展JpaRepository)測試Spring緩存支持(如here所述),實際上我的配置有問題。測試問題在Spring數據存儲庫上的Spring聲明式緩存支持

這裏是我的倉庫方法:

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

這裏是我的CacheConfiguration:

@Configuration 
@EnableCaching 
public class CacheConfiguration implements CachingConfigurer { 

    public static final String DATABASE_CACHE_NAME = "cache.database"; 

    @Bean 
    @Override 
    public CompositeCacheManager cacheManager() { 
     CompositeCacheManager cacheManager = new CompositeCacheManager(simpleCacheManager()); 
     cacheManager.setFallbackToNoOpCache(Boolean.FALSE); 
     return cacheManager; 
    } 

    @Bean 
    public CacheManager simpleCacheManager() { 
     SimpleCacheManager simpleCacheManager = new SimpleCacheManager(); 
     List<ConcurrentMapCache> cacheList = new ArrayList<>(); 
     cacheList.add(createConcurrentMapCache(60L, DATABASE_CACHE_NAME, 50L)); 
     simpleCacheManager.setCaches(cacheList); 
     return simpleCacheManager; 
    } 


    private ConcurrentMapCache createConcurrentMapCache(Long timeToLive, String name, long cacheSize) { 
     CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder().expireAfterWrite(timeToLive, TimeUnit.SECONDS); 
     if (cacheSize >= 0) { 
      cacheBuilder.maximumSize(cacheSize); 
     } 
     ConcurrentMap<Object, Object> map = cacheBuilder.build().asMap(); 
     return new ConcurrentMapCache(name, map, false); 
    } 

    @Override 
    public KeyGenerator keyGenerator() { 
     return new SimpleKeyGenerator(); 
    } 

} 

這裏是我的測試:

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

    @Before 
    public void setup() { 
     when(memberRepositoryMock.findByEmail(eq("[email protected]"))).thenReturn(MEMBER_ONE);//NPE Here 
     when(memberRepositoryMock.findByEmail(eq("[email protected]"))).thenReturn(MEMBER_TWO); 
    } 

    @Test 
    public void testFindByEmail() { 
     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]"); 
    } 

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

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

通過運行上述測試中,我注意到, SimpleKey類型的對象作爲密鑰作爲ConcurrentMapCache

@Override 
public void put(Object key, Object value) { 
    this.store.put(key, toStoreValue(value)); 
} 

在我的情況下存儲的類型爲:com.google.common.cache.LocalCache和put方法具有導致NPE空校驗....

這裏是堆棧跟蹤:

java.lang.NullPointerException 
    at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:191) 
    at com.google.common.cache.LocalCache.put(LocalCache.java:4210) 
    at org.springframework.cache.concurrent.ConcurrentMapCache.put(ConcurrentMapCache.java:121) 
    at org.springframework.cache.interceptor.CacheAspectSupport$CachePutRequest.apply(CacheAspectSupport.java:459) 
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:226) 
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:181) 
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:60) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) 
    at com.sun.proxy.$Proxy93.findByEmail(Unknown Source) 
    at com.bignibouX.tests.repository.member.MemberCachingIntegrationTest.setup(MemberCachingIntegrationTest.java:43) 
    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.RunBefores.evaluate(RunBefores.java:24) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) 
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) 
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87) 
    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:61) 
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176) 
    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) 

回答

8

你memberRepositoryMock您嘗試在使用時,()是已經與周圍的緩存建議的bean。

做這樣的事情(我不特別喜歡靜態成員,但似乎比用彈簧內部得到建議豆少醜):

// this is actuall mock 
private static MemberRepository myMock = mock(MemberRepository.class); 

// this is mock wrapped behind cache 
@Autowired 
private MemberRepository memberRepositoryMock; 

@Before 
public void setup() { 
    // using real mock for mockito stuff 
    reset(myMock); // needed for tests where spring config is cached (= mock is not recreated between tests) 
    when(myMock.findByEmail(eq("[email protected]"))).thenReturn(MEMBER_ONE);//NPE Here 
    when(myMock.findByEmail(eq("[email protected]"))).thenReturn(MEMBER_TWO); 
} 

@Test 
public void testFindByEmail() { 
    // using adviced bean in test 
    Member firstInvocation = memberRepositoryMock.findByEmail("[email protected]"); 
    assertThat(firstInvocation, is(MEMBER_ONE)); 

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

    // again real mock, not bean here 
    verify(myMock, times(1)).findByEmail("[email protected]"); 
} 

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

    @Bean 
    public MemberRepository memberRepositoryMock() { 
     return myMock; 
    } 
} 
+0

弗蘭特你好:這個問題主要是由於這樣的事實:一個空值被傳遞給緩存。 – balteo

+1

是的,我明白這一點。 null從沒有設置的模擬中得到,因爲在你的setup方法中,實際上調用了一個bean,它調用了你的模擬(它沒有設置),然後它試圖緩存返回的null值。 –