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

测试带自定义SpEL缓存键的Spring @Cacheable方法时遇SpEL异常

解决@Cacheable SpEL表达式在Mockito模拟时的空指针异常问题

你遇到的问题核心在于Mockito参数匹配器和Spring缓存切面的执行顺序冲突,导致SpEL表达式解析时参数为null,进而调用concat()方法触发异常。下面拆解原因并给出针对性解决方案:

问题根源

当你用Mockito.any(String.class)设置mock方法的返回值时,Spring缓存切面会在Mockito实际注入有效参数前就解析SpEL表达式#email.concat('.').concat(#email)。此时#email变量处于null状态,调用其concat()方法自然会抛出空指针类型的SpEL异常。

解决方案

方案1:改用SpEL安全的字符串拼接方式

替换易触发空指针的concat()方法调用,使用SpEL原生语法或Spring提供的null安全工具类:

方式A:SpEL原生字符串拼接

这种方式即使#email为null,也会生成"null.null"的缓存键,不会抛出异常:

interface MyRepo { 
    @Cacheable(value = "sample", key = "#email + '.' + #email") 
    Object findByEmail(String email); 
}

方式B:使用Spring的StringUtils工具类(推荐)

借助Spring内置的StringUtils.concat()方法,它本身已做null安全处理:

interface MyRepo { 
    @Cacheable(value = "sample", key = "T(org.springframework.util.StringUtils).concat(#email, '.', #email)") 
    Object findByEmail(String email); 
}

方案2:调整Mockito的参数匹配策略

避免使用会导致参数为null的匹配器,改用非null匹配器或具体测试值:

方式A:使用非null参数匹配器(Mockito 3.1+支持)

Mockito.when(repo.findByEmail(Mockito.anyNonNull(String.class))).thenReturn(first, second);

方式B:传入具体的测试字符串

如果测试场景允许,直接传递非null字符串,确保SpEL表达式能正常解析:

Mockito.when(repo.findByEmail("test-user@example.com")).thenReturn(first, second);

方案3:测试时临时禁用/清空缓存

如果测试重点是业务逻辑而非缓存行为,可以在测试类中临时处理缓存:

@SpringBootTest
@EnableCaching(proxyTargetClass = true)
public class MyRepoTest {
    @Autowired
    private CacheManager cacheManager;
    
    @MockBean
    private MyRepo repo;

    @BeforeEach
    void setup() {
        // 清空指定缓存
        cacheManager.getCache("sample").clear();
    }

    // 测试方法...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:08:53