如何为CharSource.read()模拟IOException?解决测试空指针异常
问题排查:Mockito模拟Resources静态方法抛出NPE而非预期UncheckedIOException
问题场景
类X的getInputString方法通过Guava的Resources工具类读取资源,捕获IOException后转抛UncheckedIOException。但用Mockito模拟Resources静态方法时,测试抛出了NullPointerException,而非预期的UncheckedIOException。
待测试代码
public class X { protected String getInputString(final String inputPath) { try { return Resources.asCharSource(Resources.getResource(inputPath), UTF_8).read(); } catch (final IOException e) { log.error("Error loading partner aliases from local config", e); throw new UncheckedIOException(e); } } }
测试代码
@Test public void Failed() throws Exception{ URL url = Resources.getResource("resources/linearPartners.json"); CharSource s; try{ s = new CharSource() { @Override public Reader openStream() throws IOException { throw new IOException("Expected as a test"); } @Override public String read() throws IOException { throw new IOException("Expected as a test"); } }; try (MockedStatic<Resources> resources = Mockito.mockStatic(Resources.class)) { resources.when(() -> Resources.asCharSource(url, UTF_8)) .thenReturn(s); Assertions.assertThrows(UncheckedIOException.class, () -> staticConfigPartnerAliasesPersistenceFacade.getInputString("resources/file.json")); } } catch (Exception e) { e.printStackTrace(); } }
错误信息
org.opentest4j.AssertionFailedError: Unexpected exception type thrown ==> expected: <java.io.UncheckedIOException> but was: <java.lang.NullPointerException> [java] org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:65) [java] org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:37) [java] org.junit.jupiter.api.Assertions.assertThrows(Assertions.java:3082)
原因分析
- 未模拟
Resources.getResource方法:在Mockito静态mock的上下文里,所有未显式模拟的静态方法调用会返回默认值(此处getResource返回null)。当getInputString调用Resources.getResource("resources/file.json")时得到null,后续传入asCharSource时触发NullPointerException。 - mock匹配逻辑不匹配:测试中用的
url是真实资源resources/linearPartners.json的URL,而测试方法传入的是resources/file.json,就算getResource返回值非null,asCharSource的mock也不会被命中。
修复方案
优化后的测试代码
@Test public void shouldThrowUncheckedIOExceptionWhenResourceLoadFails() throws Exception { // 准备一个非null的URL实例(可用任意有效URL,或mock一个) URL mockUrl = new URL("https://example.com/test.json"); try (MockedStatic<Resources> resourcesMock = Mockito.mockStatic(Resources.class)) { // 模拟getResource方法,匹配测试传入的路径 resourcesMock.when(() -> Resources.getResource("resources/file.json")) .thenReturn(mockUrl); // 模拟asCharSource方法,直接抛出IOException,无需手动实现CharSource resourcesMock.when(() -> Resources.asCharSource(mockUrl, UTF_8)) .thenThrow(new IOException("Test IO failure")); // 验证预期异常 Assertions.assertThrows(UncheckedIOException.class, () -> staticConfigPartnerAliasesPersistenceFacade.getInputString("resources/file.json")); } }
关键修复点
- 必须模拟
Resources.getResource方法,确保返回非null的URL,避免NPE。 - 让
asCharSource的mock直接抛出IOException,比手动实现CharSource更简洁且符合测试意图。 - 确保
getResource的匹配路径和测试时传入的路径一致,保证mock逻辑被命中。
内容的提问来源于stack exchange,提问作者Neha Bhoi
相关产品推荐
相关产品推荐

