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

如何Mock CloudStorageAccount.CreateCloudBlobClient()编写单元测试?

如何Mock CloudStorageAccount.CreateCloudBlobClient()来编写单元测试

你的目标方法里直接硬编码创建CloudBlobClient,这种紧耦合的写法没法直接Mock静态的CloudStorageAccount.CreateCloudBlobClient()方法。要解决这个问题,核心思路是先重构代码解耦依赖,再针对解耦后的依赖进行Mock。

1. 重构代码解耦依赖

方式一:通过构造函数注入CloudBlobClient

把CloudBlobClient的创建逻辑移到类外部,通过构造函数注入进来,这样测试时就能传入Mock的实例:

// 修改类的构造函数,注入CloudBlobClient
private readonly CloudBlobClient _cloudBlobClient;

public YourClass(CloudBlobClient cloudBlobClient)
{
    _cloudBlobClient = cloudBlobClient;
}

private async Task<BlobResultSegment> GetBlobResultSegment(string containerName, string prefix)
{
    var cc = _cloudBlobClient.GetContainerReference(containerName);
    BlobResultSegment allBlobs = await cc.ListBlobsSegmentedAsync(prefix, default);
    return allBlobs;
}

方式二:抽象Blob操作接口(更彻底的解耦)

如果想完全脱离对Azure Blob SDK具体类的依赖,可以抽象出一个Blob服务接口,让业务逻辑依赖接口而非具体实现:

// 定义接口
public interface IBlobAccessor
{
    Task<BlobResultSegment> GetBlobResultSegment(string containerName, string prefix);
}

// 实现接口
public class BlobAccessor : IBlobAccessor
{
    private readonly string _blobDataConnectionString;

    public BlobAccessor(string blobDataConnectionString)
    {
        _blobDataConnectionString = blobDataConnectionString;
    }

    public async Task<BlobResultSegment> GetBlobResultSegment(string containerName, string prefix)
    {
        CloudBlobClient cloudBlobClient = CloudStorageAccount.Parse(_blobDataConnectionString)
                                                            .CreateCloudBlobClient();
        var cc = cloudBlobClient.GetContainerReference(containerName);
        return await cc.ListBlobsSegmentedAsync(prefix, default);
    }
}

之后业务类依赖IBlobAccessor,测试时直接Mock这个接口即可,完全不用管SDK的内部逻辑。

2. 编写单元测试(以Moq框架为例)

这里用方式一的重构代码做示例,展示如何Mock整个调用链:

[TestClass]
public class YourClassTests
{
    [TestMethod]
    public async Task GetBlobResultSegment_ReturnsExpectedBlobs()
    {
        // 1. 准备模拟的Blob数据和返回段
        var mockBlobList = new List<IListBlobItem>
        {
            Mock.Of<CloudBlockBlob>(b => b.Name == "docs/file1.pdf"),
            Mock.Of<CloudBlockBlob>(b => b.Name == "docs/file2.pdf")
        };
        var mockResultSegment = new BlobResultSegment(mockBlobList, null);

        // 2. Mock CloudBlobContainer,模拟ListBlobsSegmentedAsync的返回
        var mockContainer = new Mock<CloudBlobContainer>(new Uri("https://test.blob.core.windows.net/test-container"));
        mockContainer.Setup(c => c.ListBlobsSegmentedAsync(
            It.IsAny<string>(),
            default,
            It.IsAny<BlobListingDetails>(),
            It.IsAny<int?>(),
            It.IsAny<BlobContinuationToken>(),
            It.IsAny<BlobRequestOptions>(),
            It.IsAny<OperationContext>(),
            It.IsAny<CancellationToken>()))
        .ReturnsAsync(mockResultSegment);

        // 3. Mock CloudBlobClient,模拟GetContainerReference的返回
        var mockBlobClient = new Mock<CloudBlobClient>(new Uri("https://test.blob.core.windows.net/"));
        mockBlobClient.Setup(c => c.GetContainerReference(It.IsAny<string>()))
                      .Returns(mockContainer.Object);

        // 4. 初始化被测类,传入Mock的CloudBlobClient
        var sut = new YourClass(mockBlobClient.Object);

        // 5. 执行测试方法
        var result = await sut.GetBlobResultSegment("test-container", "docs/");

        // 6. 验证结果
        Assert.IsNotNull(result);
        Assert.AreEqual(2, result.Results.Count());
        // 验证ListBlobsSegmentedAsync被正确调用
        mockContainer.Verify(c => c.ListBlobsSegmentedAsync(
            "docs/",
            default,
            It.IsAny<BlobListingDetails>(),
            It.IsAny<int?>(),
            It.IsAny<BlobContinuationToken>(),
            It.IsAny<BlobRequestOptions>(),
            It.IsAny<OperationContext>(),
            It.IsAny<CancellationToken>()), Times.Once);
    }
}

3. 不推荐直接Mock静态方法的原因

虽然可以用Microsoft Fakes、TypeMock这类工具Mock静态的CloudStorageAccount.Parse()或CreateCloudBlobClient(),但这种方式不建议:

  • 静态Mock会让测试逻辑变得复杂,容易出现不稳定的情况
  • 违背依赖倒置原则,代码的可维护性和扩展性都会变差
  • 重构依赖注入的方式更符合面向对象设计,后续迭代也更省心

内容的提问来源于stack exchange,提问作者Juhi Shrivastava

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 17:21:07