Java单元测试Mock构造器内静态方法解决空指针异常
问题根因
抛空指针的逻辑很直接:测试运行环境没有配置HOSTNAME环境变量,NodeUtils.getHostname()取到null,传到hostnameToNode方法时调用null的startsWith方法直接触发异常。你只要在实例化MyClass之前隔离掉NodeUtils.getMyNode()这个依赖外部环境的调用就行,下面给两种可落地的方案。
方案1:直接Mock静态方法(符合你当前需求)
现在Mockito 3.4.0以上版本自带的inline mock maker已经支持静态方法Mock,不用再引入PowerMock这类重型框架,操作很简单:
- 首先在测试依赖里引入
mockito-inline,Maven配置如下:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency>
- 修改测试类,注意静态Mock的初始化必须放在
new MyClass()之前,否则构造函数执行时还是会走真实方法:
package com.javaeasily.demos.junit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import static org.mockito.Mockito.mockStatic; public class MyClassTest { private MyClass myclass; private MockedStatic<NodeUtils> nodeUtilsMock; @BeforeEach public void SetUp() { // 先开启NodeUtils的静态Mock nodeUtilsMock = mockStatic(NodeUtils.class); // 指定getMyNode()返回固定测试值,不会再走读环境变量的真实逻辑 nodeUtilsMock.when(NodeUtils::getMyNode).thenReturn("test-node-01"); // 再实例化目标类 myclass = new MyClass(); } @AfterEach public void tearDown() { // 每个测试跑完关闭静态Mock,避免Mock状态污染其他测试用例 nodeUtilsMock.close(); } @Test public void testReconfigureNode() { myclass.reconfigureNode(); } }
额外提醒:你代码里的ACTIVE_SERVICES_POST_RECONFIGURE是static全局共享的列表,多个测试用例执行时会互相残留数据,建议在@BeforeEach里先执行ACTIVE_SERVICES_POST_RECONFIGURE.clear()清理历史数据,避免测试结果不稳定。
方案2:重构代码解耦静态依赖(长期维护更推荐)
从编码设计角度,不建议在构造函数里硬耦合依赖外部环境的静态方法,后续测试和维护成本都很高。你可以稍微调整构造函数逻辑,把节点值作为入参传入,完全不需要Mock:
// 构造函数直接接收节点名参数,不再内部调用静态工具方法 MyClass(String nodeName) { ACTIVE_SERVICES_POST_RECONFIGURE.add("my-node-" + nodeName); }
生产代码实例化时传入NodeUtils.getMyNode()即可,测试时直接传固定字符串比如"test-node-01"就能跑通,逻辑更透明,也不需要依赖Mock框架的静态Mock能力。
内容的提问来源于stack exchange,提问作者Kundan Kumar
相关产品推荐
相关产品推荐

