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

如何通过JUnit+Spring集成测试验证@Cacheable方法的Redis超时异常处理

如何测试自定义的CacheErrorHandler处理Redis异常

我来帮你搞定这个单元测试的难题!要验证你的GlobalCacheErrorHandler是否能正确处理Redis超时这类异常,关键是要模拟缓存操作抛出异常的场景,不用真的搭建Redis服务。下面是两种可行的方案,从简单的单元测试到集成测试都有:


方案1:直接单元测试ErrorHandler方法(快速验证)

如果你只是想验证GlobalCacheErrorHandler的方法逻辑(比如日志是否正确输出),可以直接实例化它,手动调用对应的方法,然后验证日志行为:

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.data.redis.RedisTimeoutException;
import static org.assertj.core.api.AssertionsForClassTypes.then;

class GlobalCacheErrorHandlerTest {

    private final GlobalCacheErrorHandler errorHandler = new GlobalCacheErrorHandler();
    private final Logger logger = LoggerFactory.getLogger(GlobalCacheErrorHandler.class);

    @Test
    void handleCacheGetError_shouldLogCorrectMessage() {
        // 准备模拟数据
        RedisTimeoutException timeoutException = new RedisTimeoutException("Test timeout");
        Cache mockCache = () -> "EMPLOYEE_CACHE"; // 简单模拟Cache对象

        // 调用目标方法
        errorHandler.handleCacheGetError(timeoutException, mockCache, "EId_1");

        // 验证日志是否正确输出
        then(logger).should().error("Could not get the value from cache");
    }
}

这种方式简单直接,适合快速验证ErrorHandler的基础逻辑,但无法模拟@Cacheable注解触发缓存操作的完整流程。


方案2:集成测试(模拟@Cacheable触发缓存异常)

如果你想验证当@Cacheable方法执行时,Redis异常是否会被正确传递给ErrorHandler,需要结合Spring测试框架和Mockito来模拟整个缓存流程:

步骤1:配置测试类

我们需要加载必要的Spring配置,同时替换掉真实的RedisCacheManager为模拟对象:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.cache.Cache;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.RedisTimeoutException;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(MockitoExtension.class)
@SpringBootTest(classes = {ApplicationCacheConfiguration.class})
class EmployeeRepositoryCacheErrorTest {

    @MockBean
    private RedisCacheManager redisCacheManager;

    @Autowired
    private EmployeeRepository employeeRepository;

    @Autowired
    private GlobalCacheErrorHandler cacheErrorHandler;

    @Captor
    private ArgumentCaptor<RuntimeException> exceptionCaptor;

    @Test
    void whenCacheGetThrowsTimeout_thenErrorHandlerHandlesIt() {
        // 1. 模拟RedisCache对象,让它的get方法抛出超时异常
        RedisCache mockCache = mock(RedisCache.class);
        when(mockCache.get(anyObject()))
                .thenThrow(new RedisTimeoutException("Redis connection timed out"));

        // 2. 让CacheManager返回我们模拟的缓存实例
        when(redisCacheManager.getCache("EMPLOYEE_CACHE"))
                .thenReturn(mockCache);

        // 3. Spy包装ErrorHandler,方便验证方法调用
        GlobalCacheErrorHandler spyErrorHandler = spy(cacheErrorHandler);

        // 4. 调用被@Cacheable注解的方法
        employeeRepository.findByEmplyeeId(1L);

        // 5. 验证ErrorHandler的方法是否被正确调用
        verify(spyErrorHandler).handleCacheGetError(
                exceptionCaptor.capture(),
                any(Cache.class),
                eq("EId_1")
        );

        // 6. 确认捕获的异常是RedisTimeoutException
        assertThat(exceptionCaptor.getValue())
                .isInstanceOf(RedisTimeoutException.class)
                .hasMessageContaining("Redis connection timed out");
    }
}

关键细节说明:

  • @MockBean:替换Spring容器中的真实RedisCacheManager,这样我们可以完全控制缓存的行为,让它抛出我们需要的异常。
  • Spy包装ErrorHandler:因为GlobalCacheErrorHandler是Spring创建的Bean,我们需要用Spy来包装它,才能验证它的方法是否被调用。
  • 模拟缓存操作:让RedisCache的get方法抛出RedisTimeoutException,这样当@Cacheable方法尝试从缓存获取数据时,就会触发异常,进而调用ErrorHandler。
  • 验证逻辑:用ArgumentCaptor捕获异常,确认是我们期望的Redis超时异常,同时验证handleCacheGetError方法被正确调用。

额外注意事项

  • 如果你的EmployeeRepository是Spring Data的接口(比如继承JpaRepository),记得用@MockBean替换它的实现,避免测试时调用真实数据库:
    @MockBean
    private EmployeeRepository employeeRepository;
    
    // 模拟数据库查询返回结果
    when(employeeRepository.findByEmplyeeId(anyLong()))
            .thenReturn(List.of(new Employee()));
    
  • 如果你想验证日志输出,可以使用AssertJ的日志断言工具,具体依赖和用法可以参考方案1中的示例。

这样就能完整测试你的GlobalCacheErrorHandler在Redis异常场景下的表现了!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 18:02:34