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

如何使用xunit模拟Azure Cloud Storage Account及相关代码

模拟Azure Blob存储代码及包装器实现问题

我需要模拟以下Azure Blob存储相关代码,并通过服务包装器改造现有代码,但对具体实现和模拟测试的设置感到困惑,希望能得到示例代码。

原始仓库代码

CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connString);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
CloudBlockBlob cloudBlobInstance = container.GetBlockBlobReference(imageName); // 注:原代码此处参数有误,应为imageName而非container
cloudBlobInstance.UploadFromByteArrayAsync(ImageBytes, 0, ImageBytes.Length);

注入Azure服务包装器后的目标改造代码

CloudStorageAccount cloudStorageAccountClient = _azureSeriveWrapper.GetStorageAccount("connString"); 
CloudBlobClient cloudBlobClient  = _azureSeriveWrapper.GetBlobClient(cloudStorageAccountClient); 
CloudBlobContainer cloudBlobContainer= _azureSeriveWrapper.GetBlobContainer(cloudBlobClient,"containerName");
CloudBlockBlob cloudBlobInstance = _azureSeriveWrapper.GetBlockBlobReference(cloudBlobContainer, AssetImageName); 
await cloudBlobInstance.UploadFromByteArrayAsync(ImageBytes, 0, ImageBytes.Length); // 建议添加await
mediaUrl = cloudBlobInstance.Uri.AbsoluteUri;

我编写的包装器代码

public class AzureServiceWrapper : IAzureServiceWrapper
{
    public CloudStorageAccount GetCloudStorageAccount(string connString)
    {
        CloudStorageAccount cloudStorageAccountClient = CloudStorageAccount.Parse(connString);
        return cloudStorageAccountClient;
    }
    public CloudBlockBlob GetBlockBlobReference(CloudBlobContainer storageContainer, string fileName)
    {
        return storageContainer.GetBlockBlobReference(fileName);
    }
    public CloudBlobClient GetBlobClient(CloudStorageAccount cloudStorageAccountClient)
    {      
        CloudBlobClient cloudBlobClient = cloudStorageAccountClient.CreateCloudBlobClient();
        return cloudBlobClient;
    }    

    public CloudBlobContainer GetBlobContainer(CloudBlobClient cloudBlobClient, string containerName)
    {          
        CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference(containerName);
        return cloudBlobContainer;
    }
}

完整实现方案

1. 完善接口定义

确保接口与包装器类的方法签名完全匹配:

public interface IAzureServiceWrapper
{
    CloudStorageAccount GetCloudStorageAccount(string connString);
    CloudBlobClient GetBlobClient(CloudStorageAccount cloudStorageAccountClient);
    CloudBlobContainer GetBlobContainer(CloudBlobClient cloudBlobClient, string containerName);
    CloudBlockBlob GetBlockBlobReference(CloudBlobContainer storageContainer, string fileName);
}

2. 依赖注入配置(以ASP.NET Core为例)

在Program.cs中注册服务,让框架管理包装器的实例:

// .NET 6+ Program.cs
builder.Services.AddScoped<IAzureServiceWrapper, AzureServiceWrapper>();

在业务类中通过构造函数注入使用:

public class ImageUploadService
{
    private readonly IAzureServiceWrapper _azureServiceWrapper;

    public ImageUploadService(IAzureServiceWrapper azureServiceWrapper)
    {
        _azureServiceWrapper = azureServiceWrapper;
    }

    public async Task<string> UploadImageAsync(byte[] imageBytes, string connString, string containerName, string imageName)
    {
        var storageAccount = _azureServiceWrapper.GetCloudStorageAccount(connString);
        var blobClient = _azureServiceWrapper.GetBlobClient(storageAccount);
        var container = _azureServiceWrapper.GetBlobContainer(blobClient, containerName);
        var blockBlob = _azureServiceWrapper.GetBlockBlobReference(container, imageName);

        await blockBlob.UploadFromByteArrayAsync(imageBytes, 0, imageBytes.Length);
        return blockBlob.Uri.AbsoluteUri;
    }
}

3. 模拟包装器进行单元测试(使用Moq框架)

先安装Moq NuGet包:Install-Package Moq,以下是测试示例:

using Moq;
using Xunit;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage;

public class ImageUploadServiceTests
{
    [Fact]
    public async Task UploadImageAsync_ReturnsExpectedUrl()
    {
        // 1. 构建所有依赖的模拟对象
        var mockWrapper = new Mock<IAzureServiceWrapper>();
        
        var mockStorageAccount = new Mock<CloudStorageAccount>(new StorageCredentials("testAccount", "testKey"), true);
        var mockBlobClient = new Mock<CloudBlobClient>(new Uri("https://test.blob.core.windows.net/"), mockStorageAccount.Object.Credentials);
        var mockContainer = new Mock<CloudBlobContainer>(new Uri("https://test.blob.core.windows.net/test-container"), mockBlobClient.Object.Credentials);
        
        var expectedUrl = "https://test.blob.core.windows.net/test-container/test-image.jpg";
        var mockBlockBlob = new Mock<CloudBlockBlob>(new Uri(expectedUrl), mockContainer.Object.Credentials);

        // 2. 设置包装器的模拟返回逻辑
        mockWrapper.Setup(w => w.GetCloudStorageAccount(It.IsAny<string>()))
                   .Returns(mockStorageAccount.Object);
        
        mockWrapper.Setup(w => w.GetBlobClient(mockStorageAccount.Object))
                   .Returns(mockBlobClient.Object);
        
        mockWrapper.Setup(w => w.GetBlobContainer(mockBlobClient.Object, It.IsAny<string>()))
                   .Returns(mockContainer.Object);
        
        mockWrapper.Setup(w => w.GetBlockBlobReference(mockContainer.Object, It.IsAny<string>()))
                   .Returns(mockBlockBlob.Object);

        // 3. 执行测试
        var service = new ImageUploadService(mockWrapper.Object);
        var result = await service.UploadImageAsync(new byte[] { 1,2,3 }, "testConn", "test-container", "test-image.jpg");

        // 4. 验证结果与调用次数
        Assert.Equal(expectedUrl, result);
        mockWrapper.Verify(w => w.GetCloudStorageAccount("testConn"), Times.Once);
        mockWrapper.Verify(w => w.GetBlockBlobReference(mockContainer.Object, "test-image.jpg"), Times.Once);
    }
}

关键注意事项

  • 原代码中GetBlockBlobReference的参数存在错误,已在示例中修正为imageName;
  • 异步方法UploadFromByteArrayAsync必须添加await,否则会导致操作未完成就执行后续代码;
  • 如果使用Azure新SDK(Azure.Storage.Blobs),只需替换对应的类(如BlobServiceClient、BlobContainerClient),包装器的设计思路保持一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 02:22:01