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