如何移除Java Optional的ifPresent中assertThat语句的try-catch块
解决方案
核心原因是ifPresent接收的Consumer函数式接口的accept方法不允许抛出受检异常,你代码中json.write()和objectMapper.writeValueAsString()都会抛出受检的IOException,因此编译器强制要求你在lambda内部捕获异常,不允许向上抛出。
以下是两种可直接落地的方案:
方案1:替换ifPresent为显式判空(最推荐,无额外代码侵入)
放弃lambda形式的ifPresent,改用isPresent()判断后手动取值,此时代码块不属于Consumer接口约束范围,你的测试方法已经声明了throws IOException,可直接移除内层try-catch:
@Test public void testPostRequestThenSaveResponse() throws HttpStatusCodeException, JsonProcessingException, IllegalArgumentException, IOException { // Should success. try { // 先接收Optional返回值 var responseOpt = ServiceAssignmentImpl.httpPostDeliveryOrder(url, token, sod, customerSite, customer, item, uom, driver); // 显式判空后处理 if (responseOpt.isPresent()) { DeliveryOrderResponse deliveryOrderResponse = responseOpt.get(); // increase DO number for next test. deliveryOrderNum++; DeliveryOrderResponse savedDOResponse = deliveryOrderResponseRepository .save(deliveryOrderResponse); DeliveryOrderResponse fetchedDOResponse = deliveryOrderResponseRepository .findOne(savedDOResponse.getId()); // 直接写断言,不需要try-catch,异常直接向上抛出 assertThat(json.write(fetchedDOResponse)) .isEqualTo(objectMapper.writeValueAsString(deliveryOrderResponse)); } } catch (HttpStatusCodeException e) { // need getResponseBodyAsString() to see detail error message. System.out.println("** HttpStatusCodeException: " + e.getResponseBodyAsString()); throw e; } }
如果使用Java 8/9版本,将代码中的var responseOpt替换为Optional<DeliveryOrderResponse> responseOpt即可。
这个方案同时解决了原代码的隐藏问题:原代码内层catch仅打印堆栈不抛出异常,如果序列化步骤报错,测试不会标记失败,仅会打印日志,属于漏测问题。
方案2:包装受检异常为非受检异常(保留ifPresent写法)
如果你坚持要使用ifPresent的lambda形式,可以在catch块中将IOException包装为非受检的RuntimeException抛出,此时不需要额外修改方法签名:
// ifPresent内部的断言部分改写为 try { assertThat(json.write(fetchedDOResponse)) .isEqualTo(objectMapper.writeValueAsString(deliveryOrderResponse)); } catch (IOException e) { throw new RuntimeException("序列化对比失败", e); }
注意:原代码中catch块仅打印异常不抛出的写法是错误的,会导致序列化异常时测试不会失败,无论选哪种方案都建议将异常向上抛出,让JUnit可以正确标记测试失败。
内容的提问来源于stack exchange,提问作者Constantine Lee
相关产品推荐
相关产品推荐

