使用NSubstitute和Xunit模拟Azure存储时GetBlockBlobReference返回NULL
问题原因分析
1. GetCloudBlobContainer中blobClient.GetContainerReference返回null
- 测试mock参数不匹配:业务代码里将容器名转为小写(
containerName.ToLower()),但测试中传入的是大写开头的"Tenant",导致mock未触发,真实方法被执行。 - 真实方法执行时,若连接字符串配置路径错误(比如
AzureStorageConnectionStringKey未指向"Azurestorage:AzureStorageConnectionString"),或测试环境无实际Azure存储账户支撑,会引发后续逻辑异常,表现为返回null(注:GetContainerReference本身不会返回null,实际是连接字符串解析失败或依赖未正确初始化导致的连锁问题)。
2. Upload中blobContainer.GetBlockBlobReference返回null
- 仅mock了
CloudBlobContainer对象,但未配置GetBlockBlobReference方法的返回值,NSubstitute对未显式配置的引用类型方法默认返回null。
解决方案
步骤1:修复mock参数匹配问题
修改测试中GetCloudBlobContainer的mock参数为小写,匹配业务代码的处理逻辑:
[Fact] public async Task Should_Upload_Assessment_Custom_Document() { var container = Substitute.For<CloudBlobContainer>(new Uri("http://legalregtechquestiondocs/")); // 提前mock GetBlockBlobReference的返回值 var blockBlob = Substitute.For<CloudBlockBlob>(new Uri("http://legalregtechquestiondocs/test-blob")); container.GetBlockBlobReference(Arg.Any<string>()).Returns(blockBlob); UsingDbContext(async context => { // 使用小写容器名匹配业务代码的ToLower()处理 _azureStorageHelper.GetCloudBlobContainer("tenant").Returns(container); }); var _uploadDoc = await _assessmentCutsomDocumentAppService.Upload("xyz.doc", 1001); SaveChanges(); Assert.NotNull(_uploadDoc); Assert.Equal("xyz.doc", _uploadDoc.DisplayFileName); }
步骤2:配置GetBlockBlobReference的mock返回值
在创建CloudBlobContainer的mock后,添加方法配置,让它返回mock的CloudBlockBlob对象,避免返回null:
var blockBlob = Substitute.For<CloudBlockBlob>(new Uri("http://legalregtechquestiondocs/test-blob")); // 支持任意文件名参数匹配 container.GetBlockBlobReference(Arg.Any<string>()).Returns(blockBlob); // 若需匹配特定文件名,可改用:Arg.Is<string>(name => name.Contains("xyz.doc"))
步骤3:优化业务代码可测试性(可选但推荐)
- 将
GetCloudBlobContainer从静态方法改为实例方法,通过构造函数注入IConfiguration,避免直接依赖静态逻辑:
public interface IAzureStorageHelper { Task<CloudBlobContainer> GetCloudBlobContainer(string containerName, bool isPrivate = true); } public class AzureStorageHelper : IAzureStorageHelper { private readonly IConfiguration _configuration; private const string AzureStorageConnectionStringKey = "Azurestorage:AzureStorageConnectionString"; public AzureStorageHelper(IConfiguration configuration) { _configuration = configuration; } public async Task<CloudBlobContainer> GetCloudBlobContainer(string containerName, bool isPrivate = true) { string storageConnectionString = _configuration[AzureStorageConnectionStringKey]; containerName = containerName.ToLower(); var storageAccount = CloudStorageAccount.Parse(storageConnectionString); var blobClient = storageAccount.CreateCloudBlobClient(); var container = blobClient.GetContainerReference(containerName); await container.CreateIfNotExistsAsync(); if (isPrivate) { await container.SetPermissionsAsync(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Off }); } else { await container.SetPermissionsAsync(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob }); } return container; } }
步骤4:验证连接字符串配置
确保AzureStorageConnectionStringKey的值为"Azurestorage:AzureStorageConnectionString",与appsettings中的嵌套配置路径完全匹配,避免无法读取连接字符串引发的异常。
内容的提问来源于stack exchange,提问作者San Jaisy
相关产品推荐
相关产品推荐

