缓存配置类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]
问题分析
- 条件注解未生效:直接调用
caffeineConfig()方法属于普通方法调用,@ConditionalOnProperty是Spring容器创建Bean时才会触发的条件校验,手动调用不会触发该逻辑,所以第二个测试中caffeine不会为null。 - 上下文初始化冲突:
@SpringBootTest启动Spring上下文时,@EnableCaching要求容器中必须存在CacheManager或CacheResolverBean。虽然添加了@MockBean CacheManager,但同时使用@InjectMocks手动实例化CacheConfig,导致上下文初始化逻辑和手动实例化逻辑冲突。 - 属性设置方式错误:用
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
相关产品推荐
相关产品推荐

