使用Redis Mock做单元测试时出现NullPointerException问题求助
解决RedisTemplate空指针异常问题
你的空指针异常根源很清晰:你在RegistrationService里手动new了RedisTemplate实例,但这个实例完全脱离了Spring的上下文管理,没有被配置连接工厂(ConnectionFactory)。Spring容器管理的RedisTemplate会自动注入连接工厂并完成初始化,但你自己new出来的RedisTemplate核心组件都是null,调用opsForHash()自然会抛出NPE。
下面是具体的修复步骤:
1. 改用依赖注入获取RedisTemplate,不要手动实例化
把RegistrationService里的RedisTemplate改成通过@Autowired注入,替换掉自己new的代码:
@Import({RedisConfiguration.class}) @Service public class RegistrationService { // 注入Spring容器管理的RedisTemplate实例 @Autowired private RedisTemplate<String, Object> redisTemplate; public String submitApplicationOverview(String overviewRequest) throws IOException { ObjectMapper mapper = new ObjectMapper(); HashMap<String,Object> applicationData = mapper.readValue(overviewRequest, new TypeReference<Map<String,Object>>(){}); // 序列化配置移到统一配置类中,不用在业务代码重复设置 UUID key = UUID.randomUUID(); redisTemplate.opsForHash().putAll(key.toString(), applicationData); System.out.println("Application saved:" + overviewRequest); return key.toString(); } }
(顺便提一句:方法名和变量名建议遵循Java小驼峰命名规范,我帮你调整了SubmitApplicationOverview和Key的命名)
2. 在配置类中统一配置RedisTemplate的序列化规则
创建或修改RedisConfiguration类,在这里完成RedisTemplate的序列化配置,让所有注入的实例都复用这个配置:
@Configuration public class RedisConfiguration { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); // 配置哈希键值的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); // 可选:配置全局键值的序列化规则 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.afterPropertiesSet(); return template; } }
3. 给测试环境配置嵌入式Redis的连接工厂
你已经启动了嵌入式RedisServer,但还需要让Spring注入的RedisTemplate连接到这个测试服务器。可以用@TestConfiguration来覆盖生产环境的Redis配置:
@RunWith(SpringRunner.class) @SpringBootTest public class RegistrationServiceTest { private RedisServer redisServer; @Autowired private RegistrationService registrationService; @Before public void setup() { // 启动默认端口6379的嵌入式Redis redisServer = RedisServer.newRedisServer(); redisServer.start(); } @After public void teardown() { // 测试结束后关闭Redis,避免占用端口 if (redisServer != null) { redisServer.stop(); } } // 测试专用的Redis配置,覆盖生产环境的集群/哨兵配置 @TestConfiguration static class TestRedisConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { // 连接到本地嵌入式Redis return new LettuceConnectionFactory("localhost", 6379); } } @Test public void testSubmitApplicationOverview() throws IOException { String body = "{\n" + " \"VehicleCategory\": \"CAR\",\n" + " \"EmailAddress\": \"email@email.com\"\n" + "}"; String result = registrationService.submitApplicationOverview(body); Assert.assertNotNull("返回的UUID不能为空", result); // 可选:验证Redis中是否真的存入了数据 // Map<Object, Object> storedData = redisTemplate.opsForHash().entries(result); // Assert.assertEquals("CAR", storedData.get("VehicleCategory")); } }
额外注意事项
- 你的
application.properties里同时配置了集群、哨兵、URL三种Redis连接方式,这会导致Spring无法确定使用哪种配置。生产环境要只保留符合你实际部署的配置(比如用哨兵就删掉集群和URL的配置);测试环境我们已经通过@TestConfiguration覆盖了配置,不用担心冲突。
内容的提问来源于stack exchange,提问作者Kaigo
相关产品推荐
相关产品推荐

