3、缓存过期时间配置方法:配置文件方式、注解方式、API动态设置方式
在 Testing 环境下,缓存过期时间的配置直接影响测试结果的稳定性和可重复性。不同的配置方式适用于不同的测试阶段与场景:配置文件方式适合全局统一管理,注解方式适合细粒度方法级控制,API 动态设置方式则适合在测试用例中实时调整。本章将系统讲解三种配置方法的核心原理、适用场景及 Testing 环境下的最佳实践。
Testing 环境原则: 缓存过期时间应尽量缩短(如 1-5 秒),避免因缓存未过期导致测试用例读取旧数据;同时应避免过期时间过短导致缓存频繁失效,增加数据库压力。推荐在测试基类中统一管理过期时间配置。
3.1 配置文件方式
配置文件方式是缓存过期时间管理的基础手段,通过外部化配置实现缓存策略的集中管控。在 Testing 环境下,通常使用 application-test.yml 或 application-test.properties 文件覆盖默认缓存配置。
3.1.1 Spring Boot 缓存配置示例
# application-test.yml
spring:
cache:
type: redis
redis:
time-to-live: 5s # 全局默认过期时间 5 秒
cache-null-values: false # 不缓存空值
use-key-prefix: true
key-prefix: "test:" # 测试环境前缀,便于隔离
# 针对特定缓存名称的过期时间配置(通过 CacheManager 定制)
custom:
cache:
ttl:
userCache: 3s
productCache: 10s
sessionCache: 2s
3.1.2 自定义 CacheManager 实现差异化 TTL
@Configuration
@Profile("test")
public class TestCacheConfig {
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return builder -> {
Map<String, Duration> ttlMap = new HashMap<>();
ttlMap.put("userCache", Duration.ofSeconds(3));
ttlMap.put("productCache", Duration.ofSeconds(10));
ttlMap.put("sessionCache", Duration.ofSeconds(2));
ttlMap.forEach((cacheName, ttl) ->
builder.withCacheConfiguration(cacheName,
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(ttl)
.prefixCacheNameWith("test:" + cacheName + ":")
)
);
};
}
}
| 配置方式 | 优点 | 缺点 | Testing 环境适用性 |
|---|---|---|---|
| 全局 TTL(application.yml) | 配置简单,统一管理 | 无法区分不同缓存区域 | 适合快速验证,所有缓存使用相同过期时间 |
| CacheManager 定制 | 细粒度控制,支持按缓存名称设置不同 TTL | 配置代码较多,需重启生效 | 适合回归测试,模拟不同缓存策略 |
| Profile 隔离 | 环境隔离清晰,不影响其他环境 | 需维护多套配置文件 | 推荐方式,测试环境与开发/生产完全隔离 |
避坑提示: Testing 环境下不要使用过长的缓存过期时间(如 30 分钟以上),否则测试用例执行完毕后缓存仍未过期,可能导致后续测试读取脏数据。建议测试基类中统一设置最大 TTL 不超过 10 秒。
3.2 注解方式
注解方式通过 @Cacheable、@CachePut、@CacheEvict 等注解在方法级别声明缓存策略,其中过期时间通过 cacheManager 的配置或 SpEL 表达式间接控制。Spring 原生注解不直接支持 TTL 参数,需要结合自定义注解或 @CacheConfig 实现。
3.2.1 原生注解 + CacheManager 组合
@Service
@CacheConfig(cacheNames = "userCache")
public class UserService {
// 使用 userCache 的 TTL(已在 CacheManager 中配置为 3 秒)
@Cacheable(key = "#userId")
public User getUserById(Long userId) {
// 模拟数据库查询
return userRepository.findById(userId).orElse(null);
}
// 手动清除缓存,确保测试用例读取最新数据
@CacheEvict(key = "#userId")
public void evictUserCache(Long userId) {
// 清除指定用户的缓存
}
}
3.2.2 自定义注解实现动态 TTL
为了在注解中直接指定过期时间,可以自定义注解并配合 AOP 实现:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestCacheable {
String cacheName() default "";
String key() default "";
long ttlSeconds() default 5; // 默认 5 秒
}
// AOP 切面实现
@Aspect
@Component
@Profile("test")
public class TestCacheAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Around("@annotation(testCacheable)")
public Object around(ProceedingJoinPoint joinPoint, TestCacheable testCacheable) throws Throwable {
String cacheKey = testCacheable.cacheName() + "::" + testCacheable.key();
String cachedValue = redisTemplate.opsForValue().get(cacheKey);
if (cachedValue != null) {
return deserialize(cachedValue);
}
Object result = joinPoint.proceed();
redisTemplate.opsForValue().set(cacheKey, serialize(result),
testCacheable.ttlSeconds(), TimeUnit.SECONDS);
return result;
}
}
| 注解方式 | TTL 控制方式 | 灵活性 | 测试用例编写复杂度 |
|---|---|---|---|
| @Cacheable + CacheManager | 通过 CacheManager 配置,注解不可见 | 低,TTL 固定 | 低,无需额外配置 |
| @Cacheable + SpEL | 通过 SpEL 从配置中心动态读取 | 中,支持运行时切换 | 中,需维护配置中心 |
| 自定义注解 + AOP | 注解参数直接指定 TTL | 高,每个方法可独立设置 | 中,需编写切面代码 |
Testing 最佳实践: 推荐在测试用例中使用
@CacheEvict(allEntries = true) 在 @Before 或 @After 方法中清除相关缓存,确保每个测试用例的缓存状态是确定的。例如:@CacheEvict(cacheNames = {"userCache", "productCache"}, allEntries = true)
3.3 API 动态设置方式
API 动态设置方式允许在测试用例运行时直接操作缓存客户端(如 RedisTemplate、Jedis、Lettuce),实时设置或修改缓存过期时间。这种方式灵活性最高,适合需要精确控制缓存状态的回归冒烟测试。
3.3.1 使用 RedisTemplate 动态设置 TTL
@SpringBootTest
@AutoConfigureMockMvc
public class UserCacheTest {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private UserService userService;
private static final String CACHE_KEY = "userCache::1001";
@Before
public void setup() {
// 清除可能存在的旧缓存
redisTemplate.delete(CACHE_KEY);
}
@Test
public void testCacheExpiration() throws InterruptedException {
// 1. 调用服务方法,触发缓存写入(默认 TTL 3 秒)
userService.getUserById(1001L);
// 2. 验证缓存已存在
assertNotNull(redisTemplate.opsForValue().get(CACHE_KEY));
// 3. 动态修改过期时间为 1 秒(模拟快速过期场景)
redisTemplate.expire(CACHE_KEY, 1, TimeUnit.SECONDS);
// 4. 等待 1.5 秒,缓存应自动过期
Thread.sleep(1500);
// 5. 验证缓存已过期
assertNull(redisTemplate.opsForValue().get(CACHE_KEY));
}
@Test
public void testCacheTTLExtension() {
// 1. 手动写入缓存,设置过期时间为 2 秒
redisTemplate.opsForValue().set(CACHE_KEY, "test_value", 2, TimeUnit.SECONDS);
// 2. 在过期前续期(延长到 10 秒)
redisTemplate.expire(CACHE_KEY, 10, TimeUnit.SECONDS);
// 3. 验证剩余 TTL 大于 8 秒(续期成功)
Long ttl = redisTemplate.getExpire(CACHE_KEY, TimeUnit.SECONDS);
assertTrue(ttl > 8);
}
}
3.3.2 常用 RedisTemplate API 方法
| 方法 | 功能描述 | Testing 环境典型用途 |
|---|---|---|
expire(key, timeout, unit) |
设置 key 的过期时间 | 模拟缓存快速过期、续期场景 |
getExpire(key, unit) |
获取 key 的剩余过期时间 | 验证缓存 TTL 是否符合预期 |
hasKey(key) |
检查 key 是否存在 | 验证缓存是否已被清除或过期 |
delete(key) |
删除指定 key | 测试前置清理,确保测试隔离 |
opsForValue().set(key, value, timeout, unit) |
写入缓存并指定过期时间 | 手动构造缓存数据,模拟特定状态 |
注意事项: API 动态设置方式直接操作缓存客户端,会绕过业务层的缓存注解逻辑。在回归冒烟测试中,应确保 API 操作与业务逻辑的缓存状态一致,避免出现“缓存被测试代码修改但业务代码不知情”的情况。建议在测试用例的 @After 方法中恢复缓存状态。
3.4 三种配置方式对比与选择策略
3.5 Testing 环境缓存过期时间配置清单
- 全局配置: 在
application-test.yml中设置spring.cache.redis.time-to-live: 5s - 缓存隔离: 使用
test:前缀区分测试环境缓存,避免与开发环境冲突 - 注解使用: 在 Service 层方法上使用
@Cacheable,并通过 CacheManager 配置差异化 TTL - 测试前置: 在
@Before方法中通过@CacheEvict(allEntries = true)清除相关缓存 - 动态验证: 使用
RedisTemplate.getExpire()验证缓存 TTL 是否符合预期 - 异常场景: 使用 API 方式设置极短 TTL(如 1 秒)模拟缓存过期异常
- 恢复机制: 在
@After方法中清理测试过程中动态设置的缓存,避免影响后续测试
回归冒烟测试核心要点: 缓存过期时间配置的验证应纳入回归测试用例集。每次代码变更后,执行缓存过期时间验证用例,确保:① 配置文件方式正确加载;② 注解方式 TTL 生效;③ API 动态设置能够覆盖预期场景。推荐使用参数化测试,覆盖边界值(如 TTL=0、TTL=1s、TTL=最大值)。