java 如何测试@Cacheable?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43948434/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to test @Cacheable?
提问by Grim
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
我正在努力在 Spring Boot 集成测试中测试 @Cacheable。这是我学习如何进行集成测试的第二天,我发现的所有示例都使用旧版本。我还看到了一个示例,assetEquals("some value", is())但没有使用导入语句来知道“是”属于哪个依赖项。第二次测试失败
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.
用于测试的 ApplicationContext、组件、实体、存储库和服务层。我这样做的原因是因为这个 maven 模块在其他模块中作为依赖项使用。
@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();
}
采纳答案by Tobias Otto
The first problem with SettingRepositoryITis, the @Mockanotation on the field settingRepository. This is paradox for any normal-test, integration-test or any else.
SettingRepositoryIT的第一个问题是,字段settingRepository上的@Mock注释。这对于任何正常测试、集成测试或任何其他测试来说都是矛盾的。
You should let Spring bring in the dependencies for the class-under-test, which is SettingRepositoryin your case.
您应该让 Spring 引入待测类的依赖项,在您的情况下是SettingRepository。
Please look at this example how @Autowiredis used for the class-under-test, which is OrderServicein this example:
请看这个例子@Autowired是如何用于class-under-test 的,在这个例子中是OrderService:
@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
获取完整示例的文档:§ 15. 集成测试
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?
第二个问题是您不必测试@Cachable。您应该只测试您的实现。下面是 Oliver Gierke 提供的一个很好的示例,说明您应该如何测试它:如何测试 Spring 对 Spring Data 存储库的声明性缓存支持?
回答by Constantino Cronemberger
In my case I wanted to validate the expression in the unless expression in the @Cacheable annotation, so I think it makes perfect sense and I'm not testing Spring's code.
在我的例子中,我想验证 @Cacheable 注释中的unless 表达式中的表达式,所以我认为这是完全合理的,我没有测试 Spring 的代码。
I managed to test it without using Spring Boot, so it is plain Spring test:
我设法在不使用 Spring Boot 的情况下对其进行了测试,因此它是普通的 Spring 测试:
@RunWith(SpringRunner.class)
@ContextConfiguration
public class MyTest {
@Configuration
@EnableCaching
static class Config {
@Bean
public MyCacheableInterface myCacheableInterface() {
return (authorization) -> createTestResult();
}
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("myObject");
}
}
@Autowired
private MyCacheableInterface myCacheableInterface;
In MyCacheableInterface I have the following annotation:
在 MyCacheableInterface 中,我有以下注释:
public interface MyCacheableInterface {
@Cacheable(value = "myObject", unless = "#result.?[Retorno.getSucesso() != 'S'].size() == #result.size()")
List<MyObject> businessMethod(String authorization);
}

