Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issues with testing Spring's declarative caching support on a Spring data repository

I am trying to test Spring caching support (as described here) for a Spring Data JPA repository (that extends JpaRepository) and I am actually having issues with my configuration.

Here is my repository method:

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

Here is my 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();
    }

}

Here is my test:

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

By running the above test, I noticed that a object of type SimpleKey is passed as the key and a null as the value to ConcurrentMapCache:

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

In my case the store is of type: com.google.common.cache.LocalCache and the put method has a null check resulting in a NPE....

Here is the stack trace:

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)
like image 991
balteo Avatar asked Jun 16 '14 18:06

balteo


People also ask

What kind of testing can be done in spring test module?

2.2. springframework. test. web package contains ModelAndViewAssert , which you can use in combination with JUnit, TestNG, or any other testing framework for unit tests that deal with Spring MVC ModelAndView objects.

Which of the following annotation is supported by spring for unit testing JPA components?

@JdbcTest: We can use the @JdbcTest annotation to test JPA applications, but it's for tests that only require a DataSource. The annotation configures an in-memory embedded database and a JdbcTemplate.


1 Answers

Your memberRepositoryMock that you try to use in when() is already a bean with cache advice around.

do something like this (I don't especially like the static member, but seems less ugly than getting advised bean using spring internals):

// 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;
    }
}
like image 134
František Hartman Avatar answered Sep 26 '22 22:09

František Hartman