Java 8 Lambda表达式无法触发异常,单元测试未失败原因咨询
问题分析与解决方案
这是个很常见的CompletableFuture新手坑——问题出在异步回调的异常不会自动传播到你的单元测试主线程上,我来帮你拆解清楚:
为什么单元测试没捕获到异常?
- 你在
thenAcceptBoth里写的校验逻辑是运行在异步线程中的,和单元测试执行的主线程完全分离。 - 你的单元测试方法会在调用
verifyingIndividualSubmissionValueLimits后直接执行完毕,根本不会等待异步回调的执行——也就是说,异步任务还没来得及抛出异常,测试就已经标记为“通过”了。 - 更关键的是:CompletableFuture的异步回调里抛出的异常,默认会被Future对象“吞掉”,除非你主动调用
join()/get()方法来触发异常传播。
怎么修复让单元测试能捕获到异常?
最简单的方案是修改你的方法,让它返回CompletableFuture实例,然后在单元测试中主动等待任务完成并捕获异常。
第一步:修改业务方法返回CompletableFuture
public CompletableFuture<Void> verifyingIndividualSubmissionValueLimits(String alias, double min, double max) { // ... 部分代码省略 ... CompletableFuture<Double> localPriceFuture = instrumentRequester.getMidPrice(instId); // 将thenAcceptBoth的结果返回给调用方 return localPriceFuture.thenAcceptBoth(dollarFxRateFuture, (localPrice, fxRate) -> { double minLocalValue = min * fxRate; double maxLocalValue = max * fxRate; long minQuantity = Math.round(minLocalValue / localPrice); long maxQuantity = Math.round(maxLocalValue / localPrice); if (subQty < minQuantity || subQty > maxQuantity) { log.debug("We should throw an exception because subQty is {}", subQty); throw new SubmissionValidationException(String.format("Quantity: %s, is not within %s and %s", subQty, minQuantity, maxQuantity)); } }); }
第二步:修改单元测试等待异步任务并捕获异常
@Test(expected = SubmissionValidationException.class) public void testSubmissionQuantityOutOfRange() { // 调用方法获取Future CompletableFuture<Void> validationFuture = yourService.verifyingIndividualSubmissionValueLimits("testAlias", 182, 255); // 调用join()等待异步任务完成,异常会被抛出到主线程 validationFuture.join(); }
其他可选方案
如果你的业务场景不适合返回Future,也可以在单元测试中用CountDownLatch来手动等待异步任务完成:
@Test(expected = SubmissionValidationException.class) public void testSubmissionQuantityOutOfRange() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); // 可以在回调的finally里调用latch.countDown(),或者用handle方法处理结果/异常 yourService.verifyingIndividualSubmissionValueLimits("testAlias", 182, 255); // 等待最多5秒(根据实际情况调整) latch.await(5, TimeUnit.SECONDS); }
不过这种方式不如返回Future+join()优雅,推荐优先用第一种方案。
内容的提问来源于stack exchange,提问作者ArMonk
相关产品推荐
相关产品推荐

