How to test @Cacheable?
integration-testing, java, junit, spring-test
Solution
The first problem with SettingRepositoryIT is, the @Mock anotation on the field settingRepository. This is paradox for any normal-test, integration-test or any else.
You should let Spring bring in the dependencies for the class-under-test, which is SettingRepository in your case.
Please look at this example how @Autowired is used for the class-under-test, which is OrderService in this example:
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from the
// static nested Config class
@ContextConfiguration
public class OrderServiceTest {
@Configuration
static class Config {
// this bean will be injected into the OrderServiceTest class
@Bean
public OrderService orderService() {
OrderService orderService = new OrderServiceImpl();
// set properties, etc.
return orderService;
}
}
@Autowired
private OrderService orderService;
@Test
public void testOrderService() {
// test the orderService
}
}
Go for the documentation with the full example: § 15. Integration Testing
The second problem is that you do not have to test @Cachable. You should only test your implementation. Here is a very good example from Oliver Gierke on how you should test it: How to test Spring's declarative caching support on Spring Data repositories?
Problem
I am struggling with testing @Cacheable within a Spring Boot Integration Test. This is my second day learning how to do Integration Tests and all of the examples I have found use older versions. I also saw an example of `assetEquals("some value", is())` but nothing with an import statement to know which dependency "is" belongs to. The test fails at the second This is my integration test.... ``` @RunWith(SpringRunner.class) @DataJpaTest // used for other methods @SpringBootTest(classes = TestApplication.class) @SqlGroup({ @Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:data/Setting.sql") }) public class SettingRepositoryIT { @Mock private SettingRepository settingRepository; @Autowired private Cache applicationCache; @Test public void testCachedMethodInvocation() { List<Setting> firstList = new ArrayList<>(); Setting settingOne = new Setting(); settingOne.setKey("first"); settingOne.setValue("method invocation"); firstList.add(settingOne); List<Setting> secondList = new ArrayList<>(); Setting settingTwo = new Setting(); settingTwo.setKey("second"); settingTwo.setValue("method invocation"); secondList.add(settingTwo); // Set up the mock to return *different* objects for the first and second call Mockito.when(settingRepository.findAllFeaturedFragrances()).thenReturn(firstList, secondList); // First invocation returns object returned by the method List<Setting> result = settingRepository.findAllFeaturedFragrances(); assertEquals("first", result.get(0).getKey()); // Second invocation should return cached value, *not* second (as set up above) List<Setting> resultTwo = settingRepository.findAllFeaturedFragrances(); assertEquals("first", resultTwo.get(0).getKey()); // test fails here as the actual is "second." // Verify repository method was invoked once Mockito.verify(settingRepository, Mockito.times(1)).findAllFeaturedFragrances(); assertNotNull(applicationCache.get("findAllFeaturedFragrances")); // Third invocation with different key is triggers the second invocation of the repo method List<Setting> resultThree = settingRepository.findAllFeaturedFragrances(); assertEquals(resultThree.get(0).getKey(), "second"); } } ``` ApplicationContext, components, entities, repositories and service layer for tests. The reason why I do it this way is because this maven module is used in other modules as a dependency. ``` @ComponentScan({ "com.persistence_common.config", "com.persistence_common.services" }) @EntityScan(basePackages = { "com.persistence_common.entities" }) @EnableJpaRepositories(basePackages = { "com.persistence_common.repositories" }) @SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` Cache config.... ``` @Configuration @EnableCaching public class CacheConfig { public static final String APPLICATION_CACHE = "applicationCache"; @Bean public FilterRegistrationBean registerOpenSessionInViewFilterBean() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(); OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter(); registrationBean.setFilter(filter); registrationBean.setOrder(5); return registrationBean; } @Bean public Cache applicationCache() { return new GuavaCache(APPLICATION_CACHE, CacheBuilder.newBuilder() .expireAfterWrite(30, TimeUnit.DAYS) .build()); } } ``` The repository under test.... ``` public interface SettingRepository extends JpaRepository<Setting, Integer> { @Query(nativeQuery = true, value = "SELECT * FROM Setting WHERE name = 'featured_fragrance'") @Cacheable(value = CacheConfig.APPLICATION_CACHE, key = "#root.methodName") List<Setting> findAllFeaturedFragrances(); } ```