如何使用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
相关产品推荐
相关产品推荐

