如何用JUnit5覆盖IOException捕获分支的代码测试
解决方案
方法1:传入无效的Zip字节数据(无需修改业务代码)
直接构造不符合Zip格式的字节数组,JDK原生的ZipInputStream在解析这类数据时会自动抛出IOException,刚好能覆盖catch分支,完全贴合真实异常场景。
测试代码示例(JUnit5):
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; class ServiceToTestTest { @Test void unzip_shouldCatchIOException_whenInvalidZipData() { ServiceToTest service = new ServiceToTest(); // 传入一段非Zip格式的随机字节 byte[] invalidZip = new byte[]{0x00, 0x01, 0x02, 0x03}; // 执行方法,无未捕获异常说明catch分支生效 assertDoesNotThrow(() -> service.unzip(invalidZip)); // 若catch内有自定义逻辑,可在此添加对应断言(比如验证日志、状态变更等) } }
方法2:重构代码解耦依赖(适合复杂测试场景)
如果需要灵活控制异常触发时机(比如在read阶段抛异常而非初始化阶段),可以把ZipInputStream的创建逻辑抽成可注入的工厂类,用Mockito mock工厂返回异常流。
重构后的业务代码
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.io.ByteArrayOutputStream; public class ServiceToTest { // 注入Zip流工厂,默认使用实现类 private final ZipStreamFactory zipStreamFactory; // 无参构造,兼容原有调用逻辑 public ServiceToTest() { this.zipStreamFactory = new DefaultZipStreamFactory(); } // 带参构造,用于测试注入mock实例 public ServiceToTest(ZipStreamFactory zipStreamFactory) { this.zipStreamFactory = zipStreamFactory; } public void unzip(byte[] zipFile) { try (ZipInputStream zipInputStream = zipStreamFactory.createZipStream(new ByteArrayInputStream(zipFile))) { ZipEntry entry; while ((entry = zipInputStream.getNextEntry()) != null) { byte[] buffer = new byte[1024]; int len; try (var file = new ByteArrayOutputStream(buffer.length)) { while ((len = zipInputStream.read(buffer)) > 0) { file.write(buffer, 0, len); } System.out.println(entry.getName()); } } } catch (IOException e) { System.out.println(e.getMessage()); // some logic or rethrow the exception } } // 定义Zip流工厂接口 public interface ZipStreamFactory { ZipInputStream createZipStream(InputStream inputStream) throws IOException; } // 默认实现类,保持原有逻辑 static class DefaultZipStreamFactory implements ZipStreamFactory { @Override public ZipInputStream createZipStream(InputStream inputStream) throws IOException { return new ZipInputStream(inputStream); } } }
对应的测试代码(Mockito + JUnit5)
import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipInputStream; import static org.mockito.Mockito.*; class ServiceToTestTest { @Test void unzip_shouldCatchIOException_whenZipStreamThrows() throws IOException { // mock工厂和ZipInputStream实例 ServiceToTest.ZipStreamFactory mockFactory = Mockito.mock(ServiceToTest.ZipStreamFactory.class); ZipInputStream mockZipStream = Mockito.mock(ZipInputStream.class); // 配置工厂返回mock的Zip流 when(mockFactory.createZipStream(any(InputStream.class))).thenReturn(mockZipStream); // 模拟调用getNextEntry时抛出IOException when(mockZipStream.getNextEntry()).thenThrow(new IOException("Test IO Exception")); ServiceToTest service = new ServiceToTest(mockFactory); service.unzip(new byte[0]); // 验证异常触发逻辑被执行 verify(mockZipStream, times(1)).getNextEntry(); } }
为什么不推荐传null?
传null会触发NullPointerException,和需要覆盖的IOException不属于同一异常类型,且业务代码处理的是IO相关异常,用无效Zip数据更贴合真实业务场景,测试逻辑也更合理。
内容的提问来源于stack exchange,提问作者BugsOverflow
相关产品推荐
相关产品推荐

