测试带自定义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
相关产品推荐
相关产品推荐

