Groovy测试中如何让InputStream的readAllBytes方法抛出IOException?
测试Java 11 InputStream异常处理的Groovy测试问题
待测试的Java 11代码
final InputStream pdfDocument = myService.generate(manifest); final byte[] pdfDocumentBytes; try { pdfDocumentBytes = pdfDocument.readAllBytes(); } catch (IOException e) { log.error("Unable to read content", e); throw new MyServiceException(MyServiceExceptionKey, MessageSeverity.ERROR, HttpStatus.BAD_REQUEST); }
现有Groovy 3.0.9测试方案
方案1(无异常抛出,后续触发NPE)
def exception = {throw new IOException()} def pdfDocument = Spy(InputStream.class) pdfDocument.readAllBytes() >> exception
方案2(触发Groovy错误)
InputStream pdfDocument = Mock(InputStream.class) PowerMockito.when(pdfDocument.readAllBytes()).thenThrow(exception)
问题说明
需要测试上述Java代码中的IOException异常处理逻辑,但现有方案均失效:
- 方案1使用Spy时,
readAllBytes()未抛出预期异常,代码执行后续逻辑触发空指针; - 方案2使用PowerMockito时,报错提示
when()无可用调用方法; - 暂无法承担切换到JUnit测试框架的成本,寻求Groovy环境下的解决方案,同时想了解升级Groovy版本是否有用。
解决方案
1. 升级Groovy版本是有效的
Groovy 3.0.9对Java 9+新增的默认方法(如InputStream.readAllBytes())的mock支持存在缺陷,升级到Groovy 4.x配合Spock 2.x后,默认方法的mock逻辑会得到完善。调整后的测试代码可正常触发异常:
def pdfDocument = Spy(InputStream) { readAllBytes() >> { throw new IOException() } }
2. 不升级的替代方案
方案A:调整PowerMockito用法
由于readAllBytes()是默认方法,需改用doThrow语法替代when...thenThrow,同时确保PowerMockito配置正确:
// 测试类需添加注解 @RunWith(PowerMockRunner.class) @PrepareForTest(InputStream.class) // 测试方法内代码 InputStream pdfDocument = PowerMockito.mock(InputStream.class); PowerMockito.doThrow(new IOException()).when(pdfDocument).readAllBytes();
方案B:自定义InputStream子类
手动实现一个抛出异常的InputStream子类,绕过mock框架的默认方法支持问题:
class ThrowingInputStream extends InputStream { @Override int read() throws IOException { throw new IOException(); } @Override byte[] readAllBytes() throws IOException { throw new IOException(); } } // 测试时返回该子类实例 def myService = Mock(MyService) myService.generate(_) >> new ThrowingInputStream()
总结
升级Groovy到4.x可以彻底解决默认方法的mock问题;若暂时无法升级,通过调整PowerMockito语法或自定义子类,完全可以正常测试该异常处理逻辑,不存在无法测试的情况。
内容的提问来源于stack exchange,提问作者TechGnome
相关产品推荐
相关产品推荐

