JUnit测试替换@RunWith为@ExtendWith后出现空指针异常
空指针异常根因
从@RunWith(MockitoJUnitRunner.class)切换到@ExtendWith(MockitoExtension.class)后出现空指针,是三个问题共同导致的:
- JUnit版本混用不兼容:测试类继承了JUnit 3的基类
junit.framework.TestCase,导入的@Test、@Test(expected = ...)是JUnit 4的注解,而@ExtendWith是JUnit 5(Jupiter)专属的扩展注解,三套测试框架的生命周期逻辑完全冲突。MockitoExtension只会在纯JUnit 5的运行环境下触发Mock初始化逻辑,混用框架时@Mock、@InjectMocks标注的字段不会被初始化,全部为null,自然触发空指针。之前用的MockitoJUnitRunner是JUnit 4专属的Runner,能兼容原有的JUnit4+JUnit3继承结构,所以之前可以正常运行。 Key类型的Mock对象未初始化:测试类中Key key字段的@Mock注解被注释,该字段始终为null。你仅在第一个测试方法中尝试通过反射给被测类注入不存在的key字段(被测类的key是方法内局部变量,根本没有类级别的key字段,这个反射操作本身无效),第二个测试方法完全没有处理key的null值,执行到Mockito.when(key.deriveTwiceAndDecryptLocal(...))时直接触发空指针。- 被测类的配置值未注入:被测类
IdpsDecryptorImpl中的idpsKey是通过@Value注入的私有字段,你在测试类中定义的private String idpsKey = "testKey"是测试类自身的字段,和被测类没有关联。你MockidpsClient.newKeyHandleLatest(idpsKey)时传入的是测试类的"testKey",但被测类实际调用方法时传入的是自身未初始化的idpsKey(值为null),和Mock的参数不匹配,Mock不会生效,会返回Mockito默认的null值,后续调用keyHandle的方法时同样会触发空指针。
修复方案
按以下步骤调整测试代码即可:
- 统一JUnit版本为JUnit 5,删除
extends TestCase的继承逻辑,将所有JUnit 4的导入替换为JUnit 5的对应类:- 把
org.junit.Test替换为org.junit.jupiter.api.Test - 把
org.junit.Assert替换为org.junit.jupiter.api.Assertions - 移除
@Test(expected = StorageException.class)的写法,改用JUnit 5的assertThrows做异常断言
- 把
- 给
Key key字段加上@Mock注解,删除无效的反射注入key字段的代码,被测类的Key实例是方法返回值,不需要反射注入。 - 每个测试方法执行前,通过反射给被测类注入
idpsKey的测试值。 - 删除异常测试方法中
assertNull的无效代码——方法抛出异常后后续逻辑不会执行,这段断言永远不会被触发。
修复后的测试代码参考:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @ExtendWith(MockitoExtension.class) public class IdpsDecryptorImplTest { @InjectMocks private IdpsDecryptorImpl idpsDecryptorimpl; @Mock Key key; @Mock IdpsClient idpsClient; private final String idpsKey = "testKey"; // 每个测试执行前注入配置值 @org.junit.jupiter.api.BeforeEach void setUp() { ReflectionTestUtils.setField(idpsDecryptorimpl, "idpsKey", idpsKey); } @Test public void testDecrypt() throws IdpsCommunicationException, IdpsException { byte[] encryptedBytes = new byte[2]; encryptedBytes[0] = 20; encryptedBytes[1] = 30; byte[] decryptedBytes = new byte[2]; decryptedBytes[0] = 97; decryptedBytes[1] = 98; Mockito.when(idpsClient.newKeyHandleLatest(idpsKey)).thenReturn(key); Mockito.when(key.deriveTwiceAndDecryptLocal(encryptedBytes)).thenReturn(decryptedBytes); assertNotNull(idpsDecryptorimpl.decrypt(encryptedBytes)); } @Test public void testDecryptException() throws IdpsCommunicationException, IdpsException { byte[] encryptedBytes = new byte[1]; encryptedBytes[0] = 20; Mockito.when(idpsClient.newKeyHandleLatest(idpsKey)).thenReturn(key); Mockito.when(key.deriveTwiceAndDecryptLocal(encryptedBytes)).thenThrow(new IdpsException()); assertThrows(StorageException.class, () -> idpsDecryptorimpl.decrypt(encryptedBytes)); } }
内容的提问来源于stack exchange,提问作者Satish Prasad
相关产品推荐
相关产品推荐

