You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cache测试中缓存未填充问题排查求助

Spring Cache测试无数据存入的排查与解决

问题描述

我编写了如下测试用例,用于断言数据已存入Spring Cache:

@Slf4j
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = MainSpringApplication.class)
class NamesServiceCacheTest {

    @Mock
    ApiClient apiClientMock;

    NamesServiceImpl sut;

    @Autowired
    private CacheManager cacheManager;


    @BeforeEach
    void setUp() {
        sut = new NamesServiceImpl(apiClientMock);
    }

    @Configuration
    @EnableCaching
    static class Config {
        @Bean
        public CacheManager cacheManager() {
            SimpleCacheManager cacheManager = new SimpleCacheManager();
            List<Cache> caches = new ArrayList<>();
            caches.add(new ConcurrentMapCache("namesCache"));
            cacheManager.setCaches(caches);
            return cacheManager;
        }
    }

    @Test
    void testResponseGoesIntoCache() {
        when(apiClientMock.getNamesFromXApi()).thenReturn(getMockedResponse());
        sut.getNames();
        var cache = cacheManager.getCache("namesCache"); // 调试时此处缓存为空
        assertEquals(getMockedResponse(), cache.get("namesCache").get());
    }

但测试中无数据存入缓存。怀疑是CacheManager未在Spring上下文正确加载,或是配置类继承CachingConfigurerSupport导致问题。

全局缓存配置代码:

@Configuration
@EnableCaching
public class CachingConfig extends CachingConfigurerSupport {
    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        List<Cache> caches = new ArrayList<>();
        caches.add(new ConcurrentMapCache("namesCache"));
        cacheManager.setCaches(caches);
        return cacheManager;
    }
}

服务类代码:

@Slf4j
@Service
@RequiredArgsConstructor
public class NamesServiceImpl implements NameService {

    private final ApiClient apiClient;

    @Override
    @Cacheable("namesCache")
    public Mono<GetInstitutionsResponse> getNames() {
        return apiClient.getNamesFromXApi();
    }

}

核心原因分析

1. 手动实例化的服务类无Spring代理,缓存注解不生效

setUp里直接new NamesServiceImpl(apiClientMock)创建的实例,不属于Spring容器托管对象。Spring的@Cacheable依赖AOP代理实现缓存增强,只有Spring上下文管理的Bean才会触发缓存逻辑,手动new的对象不会走缓存切面。

2. Reactor Mono未订阅,缓存逻辑未执行

getNames()返回Mono异步类型,Reactor数据流只有在被订阅(比如调用subscribe()、block())时才会执行。测试里仅调用sut.getNames()但未订阅,导致API调用和缓存逻辑都没触发,自然没有数据存入缓存。

3. 缓存Key使用错误

测试中用cache.get("namesCache")获取缓存,但@Cacheable默认Key规则是:无参数方法的默认Key为SimpleKey.EMPTY,而非缓存名称"namesCache"。就算缓存生效,用这个Key也取不到数据。

4. 测试内部配置与全局配置冲突

测试类内部定义了Config类,同时项目存在全局CachingConfig,可能导致Spring加载多个CacheManager实例。服务类实际绑定的是全局配置的CacheManager,但测试注入的是内部配置的实例,导致检查的缓存容器没有数据。

修复方案

1. 让Spring管理服务类实例

弃用手动new对象,改用@MockBean替换Spring容器中的ApiClient,直接注入NamesServiceImpl:

@Slf4j
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = MainSpringApplication.class)
class NamesServiceCacheTest {

    @MockBean
    ApiClient apiClientMock;

    @Autowired
    NamesServiceImpl sut;

    @Autowired
    private CacheManager cacheManager;

    // 移除手动实例化的setUp方法

    @Test
    void testResponseGoesIntoCache() {
        when(apiClientMock.getNamesFromXApi()).thenReturn(getMockedResponse());
        // 订阅Mono触发流程执行
        sut.getNames().block();
        // 使用正确的Key获取缓存
        var cacheValue = cacheManager.getCache("namesCache").get(SimpleKey.EMPTY).get();
        assertEquals(getMockedResponse(), cacheValue);
    }
}

2. 确保Mono被订阅

测试中调用block()(适合测试场景,阻塞直到异步流程完成)或subscribe()触发数据流执行:

sut.getNames().block();

3. 使用正确的缓存Key

要么遵循默认规则用SimpleKey.EMPTY取值,要么给@Cacheable指定明确的Key:

// 服务类指定自定义Key
@Cacheable(value = "namesCache", key = "'all-names'")
public Mono<GetInstitutionsResponse> getNames() {
    return apiClient.getNamesFromXApi();
}

// 测试用对应Key获取
var cacheValue = cacheManager.getCache("namesCache").get("all-names").get();

4. 统一缓存配置

移除测试类内部的Config,直接使用全局CachingConfig;或者将测试内部配置改为@TestConfiguration,避免与全局配置冲突:

// 测试内部配置改为TestConfiguration,仅在当前测试生效
@TestConfiguration
@EnableCaching
static class Config {
    @Bean
    public CacheManager cacheManager() {
        // 缓存配置逻辑不变
    }
}

内容的提问来源于stack exchange,提问作者Kaigo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:31:12