【记录】用JUnit 4的@Test注解时报错java.lang.NullPointerException的原因与解决方法

项目场景:

在练习黑马点评的逻辑过期解决缓存击穿时,编写了一个预热缓存数据的单元测试

java 复制代码
@SpringBootTest
public class HmDianPingApplicationTests {

    @Resource
    private ShopServiceImpl shopService;
    @Test
    public void testSaveShop() throws InterruptedException {
        shopService.saveShop2Redis(1L,10L);
    }
    }

运行时报空指针异常

bash 复制代码
java.lang.NullPointerException: Cannot invoke "com.hmdp.service.impl.ShopServiceImpl.saveShop2Redis(java.lang.Long, java.lang.Long)" because "this.shopService" is null

原因分析:

这是由于选择的@Test注解选错了,应该选择org.junit.jupiter.api的那一个而不是org.junit的那一个。前者对应JUnit 5单元测试框架 后者对应JUnit 4单元测试框架。两者的对比如下:

JUnit 4: org.junit.Test

  • 用途 : JUnit 4 提供的基础单元测试注解,用于标记测试方法。

  • 特点: 不支持重复测试、参数化测试等高级功能(需要额外扩展)。

  • 测试方法必须是 public 的。(这也是为什么如果用了这个注解,测试方法没加public的话就没有启动运行的绿色箭头的原因)

  • 必须配合 JUnit 4 的测试运行器使用(如 @RunWith)。

JUnit 5: org.junit.jupiter.api.Test

  • 用途: JUnit 5(JUnit Jupiter)提供的现代化测试注解,是 JUnit 的全新版本。

  • 特点: 无需方法是public(包级权限或 protected 也可)。

  • 支持更多功能,如重复测试(@RepeatedTest)、参数化测试(@ParameterizedTest)。

    更强的扩展性,支持条件测试(如 @EnabledIf)。

  • 无需 @RunWith,改用 @ExtendWith 或直接支持 Spring的测试环境。

JUnit 4 中,Spring 和测试的整合依赖于 @RunWith(SpringRunner.class)(或较早的 SpringJUnit4ClassRunner)。

如果没有添加 @RunWith(SpringRunner.class) 注解,Spring 的测试上下文不会加载,@SpringBootTest 的功能无法生效。

结果是,@Resource(或 @Autowired)的依赖注入不会生效,导致 shopService 为 null。

JUnit 5 使用 SpringExtension(通过 @ExtendWith(SpringExtension.class) 内部实现)来加载 Spring 测试上下文。

@SpringBootTest 会自动应用 SpringExtension,所以无需显式声明 @ExtendWith,Spring 上下文会被正确加载。

因此,在 JUnit 4 中,如果你遗漏了 @RunWith(SpringRunner.class),Spring 的功能不会生效,而在 JUnit 5 中,这一步是自动完成的。

所以为什么 shopService 是 null呢?

bash 复制代码
shopService 的注入依赖于 Spring 容器。如果 Spring 测试上下文没有正确加载:
@Resource 或 @Autowired 不会生效。
导致 shopService 没有被注入,值为 null。
调用 shopService.saveShop2Redis(...) 时自然会抛出 NullPointerException。
在 JUnit 4 中,如果没有正确使用 @RunWith(SpringRunner.class):
Spring 测试上下文未加载,shopService 不会被注入。
这也是为什么用 JUnit 4 会抛出 NullPointerException,而 JUnit 5 不会的根本原因。

解决方案:

方法一 :修复JUnit 4的问题 ,在使用时添加@RunWith(SpringRunner.class)

java 复制代码
@RunWith(SpringRunner.class) // 启用 Spring 的测试功能
@SpringBootTest
public class HmDianPingApplicationTests {

    @Resource
    private ShopServiceImpl shopService;

    @Test
    public void testSaveShop() {
        shopService.saveShop2Redis(1L, 10L);
    }
}

方法二:优先选用JUnit 5的Test注解

相关推荐
python零基础入门小白6 分钟前
【万字长文】大模型应用开发:意图路由与查询重写设计模式(从入门到精通)
java·开发语言·设计模式·语言模型·架构·大模型应用开发·大模型学习
一个天蝎座 白勺 程序猿23 分钟前
Apache IoTDB(10):数据库操作——从查询到优化的全链路实践指南
数据库·apache·时序数据库·iotdb
高山上有一只小老虎23 分钟前
构造A+B
java·算法
学困昇25 分钟前
C++中的异常
android·java·c++
MC丶科39 分钟前
Java设计模式漫画英雄宇宙-工厂模式 —Factory博士的“超级英雄制造机”!
java·设计模式·漫画
虎子_layor1 小时前
告别Redis瓶颈:Caffeine本地缓存优化实战指南
java·后端
普普通通的南瓜1 小时前
IP证书在关键信息基础设施安全防护中的实践与挑战
网络·数据库·网络协议·tcp/ip·安全·ssl
q***98521 小时前
什么是Spring Boot 应用开发?
java·spring boot·后端
带刺的坐椅1 小时前
Solon AI 开发学习4 - chat - 模型实例的构建和简单调用
java·ai·chatgpt·solon
hadage2331 小时前
--- JavaScript 的一些常用语法总结 ---
java·前端·javascript