You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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集成:

  1. 初始化异步容器客户端:
BlobAsyncContainerClient blobAsyncContainerClient = new BlobServiceClientBuilder()
    .connectionString("your-connection-string")
    .buildAsyncClient()
    .getBlobContainerClient("your-container-name");
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 18:21:09