在Quarkus应用中使用Vert.x executeBlocking后Blob下载测试失败如何解决?
问题分析
测试失败的核心原因有两点:
- 异常处理不完整:
executeBlocking内部抛出的mock异常(UncheckedIOException)未被转换为你定义的BlobProblem,直接传递给了emitter,导致测试断言的异常类型不匹配。 Uni创建方式冗余:手动使用emitter增加了复杂度,没有利用Mutiny与Vert.x的集成简化异步流处理。
同步客户端代码调整
修改downloadSingle方法,统一处理所有异常并简化Uni流:
public Uni<File> downloadSingle(String blobName) { return Uni.createFrom().call(() -> { try { Path tempDir = Files.createTempDirectory(UUID.randomUUID().toString()); // 用executeBlocking执行阻塞操作,捕获并转换异常 return vertx.executeBlocking(() -> { blobContainerClient.getBlobClient(blobName).downloadToFile(tempDir + "/" + blobName); return tempDir.toFile(); }, false) .onFailure(throwable -> throwable instanceof IOException || throwable instanceof UncheckedIOException) .recoverWithUni(throwable -> Uni.createFrom().failure(new BlobProblem( new ProblemDetail( "BLOB_DOWNLOAD_PROBLEM", "A problem occurred while downloading a blob.", Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), List.of() ) ))); } catch (IOException | UncheckedIOException e) { return Uni.createFrom().failure(new BlobProblem( new ProblemDetail( "BLOB_DOWNLOAD_PROBLEM", "A problem occurred while creating temporary directory.", Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), List.of() ) )); } }); }
关键调整:
- 将临时目录创建纳入Uni流,统一处理IO异常。
- 移除手动emitter管理,直接用
executeBlocking返回的结果构建Uni。 - 通过
onFailure().recoverWithUni()将executeBlocking内部的IO异常转换为BlobProblem,匹配测试断言要求。
测试代码优化
测试逻辑本身无大问题,可调整断言顺序让逻辑更清晰:
@Test @RunOnVertxContext void BlobHandlerTest_errorsShouldBeHandledWhenDownloadingABlob() { when(blobContainerClient.getBlobClient(any())).thenReturn(blobClient); when(blobClient.downloadToFile(any())).thenThrow(new UncheckedIOException(new IOException())); blobHandler.downloadSingle("blob-name") .subscribe().withSubscriber(UniAssertSubscriber.create()) .awaitFailure() .assertFailedWith(BlobProblem.class) .assertFailed(throwable -> { BlobProblem problem = (BlobProblem) throwable; return "BLOB_DOWNLOAD_PROBLEM".equals(problem.getProblemDetail().getCode()); }); }
Azure异步客户端正确使用方案
Azure SDK提供的异步客户端(BlobAsyncContainerClient)无需executeBlocking,可直接与Mutiny集成:
- 初始化异步容器客户端:
BlobAsyncContainerClient blobAsyncContainerClient = new BlobServiceClientBuilder() .connectionString("your-connection-string") .buildAsyncClient() .getBlobContainerClient("your-container-name");
- 修改
downloadSingle方法:
public Uni<File> downloadSingle(String blobName) { return Uni.createFrom().call(() -> { try { Path tempDir = Files.createTempDirectory(UUID.randomUUID().toString()); File targetFile = tempDir.resolve(blobName).toFile(); // 将Azure异步客户端返回的Mono转换为Uni return Uni.createFrom().publisher( blobAsyncContainerClient.getBlobAsyncClient(blobName) .downloadToFileAsync(targetFile.getAbsolutePath()) ) .replaceWith(targetFile) .onFailure(throwable -> throwable instanceof IOException || throwable instanceof UncheckedIOException) .recoverWithUni(throwable -> Uni.createFrom().failure(new BlobProblem( new ProblemDetail( "BLOB_DOWNLOAD_PROBLEM", "A problem occurred while downloading a blob.", Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), List.of() ) ))); } catch (IOException | UncheckedIOException e) { return Uni.createFrom().failure(new BlobProblem( new ProblemDetail( "BLOB_DOWNLOAD_PROBLEM", "A problem occurred while creating temporary directory.", Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), List.of() ) )); } }); }
优势:
- 异步方法不阻塞Vert.x事件循环线程,性能更优。
- 无需手动处理线程调度,测试不会出现阻塞问题。
内容的提问来源于stack exchange,提问作者Ricardo de Vries
相关产品推荐
相关产品推荐

