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

缓存配置类Mock失败,求正确的单元测试实现方案

缓存配置类单元测试问题排查与修复

问题背景

我需要为尚未Mock缓存配置属性类的可缓存层编写单元测试,自己实现的测试类运行时抛出异常,请求排查修复。

原缓存配置代码(CacheConfig.java)

import com.github.benmanes.caffeine.cache.Caffeine;

@Configuration
@EnableCaching
public class CacheConfig {
    private final CacheProperties cacheProperties;

    public CacheConfig(CacheProperties cacheProperties) {
        this.cacheProperties = cacheProperties;
    }
    
    @Bean
    @ConditionalOnProperty(name = "spring.cache.type", havingValue = "caffeine")
    public Caffeine<Object, Object> caffeineConfig() {
        return Caffeine.from(cacheProperties.getCaffeine().getSpec());
    }
}

原测试代码(CacheConfigTest.java)

@SpringBootTest(classes = CacheConfig.class)
public class CacheConfigTest {

    @MockBean
    private CacheProperties cacheProperties;

    @MockBean
    private CacheManager cacheManager;

    @InjectMocks
    private CacheConfig cacheConfig;

    @Value("${spring.cache.type}")
    private String cacheType;

    @BeforeEach
    void setUp() {
        // Mock the behavior of cacheProperties
        when(cacheProperties.getCaffeine()).thenReturn(new CacheProperties.Caffeine());
        when(cacheProperties.getCaffeine().getSpec()).thenReturn("expireAfterWrite=10m");
    }

    @Test
    void testCaffeineConfigBeanCreationWhenCacheTypeIsCaffeine() {
        // Arrange
        System.setProperty("spring.cache.type", "caffeine");

        // Act
        Caffeine<Object, Object> caffeine = cacheConfig.caffeineConfig();

        // Assert
        assertThat(caffeine).isNotNull();
        verify(cacheProperties, times(1)).getCaffeine();
    }

    @Test
    void testCaffeineConfigBeanNotCreatedWhenCacheTypeIsNotCaffeine() {
        // Arrange
        System.setProperty("spring.cache.type", "other");

        // Act
        Caffeine<Object, Object> caffeine = cacheConfig.caffeineConfig();

        // Assert
        assertThat(caffeine).isNull();
    }
}

异常栈信息

Caused by: java.lang.IllegalStateException: No CacheResolver specified, and no bean of type CacheManager found. Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.
    at org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated(CacheAspectSupport.java:277) ~[spring-context-6.1.6.jar:6.1.6]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:986) ~[spring-beans-6.1.6.jar:6.1.6]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.6.jar:6.1.6]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.6.jar:6.1.6]

问题分析

  1. 条件注解未生效:直接调用caffeineConfig()方法属于普通方法调用,@ConditionalOnProperty是Spring容器创建Bean时才会触发的条件校验,手动调用不会触发该逻辑,所以第二个测试中caffeine不会为null。
  2. 上下文初始化冲突:@SpringBootTest启动Spring上下文时,@EnableCaching要求容器中必须存在CacheManager或CacheResolver Bean。虽然添加了@MockBean CacheManager,但同时使用@InjectMocks手动实例化CacheConfig,导致上下文初始化逻辑和手动实例化逻辑冲突。
  3. 属性设置方式错误:用System.setProperty设置属性的时机不对,Spring上下文启动后修改系统属性无法触发配置刷新,属性不会生效。

修复方案

方案1:纯单元测试(验证方法逻辑,无需启动Spring上下文)

适合快速测试caffeineConfig()方法的业务逻辑,仅用Mockito模拟依赖:

import com.github.benmanes.caffeine.cache.Caffeine;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class CacheConfigTest {

    @Mock
    private CacheProperties cacheProperties;

    @InjectMocks
    private CacheConfig cacheConfig;

    @BeforeEach
    void setUp() {
        CacheProperties.Caffeine caffeineProps = new CacheProperties.Caffeine();
        caffeineProps.setSpec("expireAfterWrite=10m");
        when(cacheProperties.getCaffeine()).thenReturn(caffeineProps);
    }

    @Test
    void caffeineConfig_returnsValidCaffeineInstance() {
        // Act
        Caffeine<Object, Object> caffeine = cacheConfig.caffeineConfig();

        // Assert
        assertThat(caffeine).isNotNull();
        verify(cacheProperties, times(1)).getCaffeine();
        verify(cacheProperties.getCaffeine(), times(1)).getSpec();
    }
}

方案2:条件验证测试(启动Spring上下文,验证Bean创建逻辑)

适合测试@ConditionalOnProperty的生效逻辑,验证不同缓存类型下Bean是否正确创建:

import com.github.benmanes.caffeine.cache.Caffeine;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.when;

@SpringBootTest(classes = CacheConfig.class)
public class CacheConfigConditionalTest {

    @MockBean
    private CacheProperties cacheProperties;

    private final ApplicationContext applicationContext;

    public CacheConfigConditionalTest(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @BeforeEach
    void setUp() {
        CacheProperties.Caffeine caffeineProps = new CacheProperties.Caffeine();
        caffeineProps.setSpec("expireAfterWrite=10m");
        when(cacheProperties.getCaffeine()).thenReturn(caffeineProps);
    }

    @Test
    @TestPropertySource(properties = "spring.cache.type=caffeine")
    void caffeineConfigBeanExistsWhenCacheTypeIsCaffeine() {
        Caffeine<Object, Object> caffeine = applicationContext.getBean(Caffeine.class);
        assertThat(caffeine).isNotNull();
    }

    @Test
    @TestPropertySource(properties = "spring.cache.type=other")
    void caffeineConfigBeanDoesNotExistWhenCacheTypeIsNotCaffeine() {
        assertThatThrownBy(() -> applicationContext.getBean(Caffeine.class))
                .isInstanceOf(NoSuchBeanDefinitionException.class);
    }
}

关键修复点

  • 移除@InjectMocks和手动调用方法的逻辑,改用Spring上下文获取Bean来验证条件注解效果;
  • 使用@TestPropertySource设置测试属性,确保Spring上下文能正确读取配置;
  • 拆分测试场景:纯单元测试验证方法逻辑,上下文测试验证条件Bean创建逻辑,职责更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 10:09:52