Azure File Share CreateAsync方法单元测试Mock配置问题排查
Azure文件共享创建方法的单元测试Mock配置错误分析
原业务方法代码
public async Task<ShareClient> CreateFileShare(string shareName) { if (!ShareNameValidator.IsValid(shareName)) { throw new ArgumentException("Invalid share name."); } ShareClient shareFileClient = _shareServiceClient.GetShareClient(shareName.ToLowerInvariant()); if (await shareFileClient.ExistsAsync()) { throw new ShareExistsException("Share already exists."); } await shareFileClient.CreateAsync(); return shareFileClient; }
单元测试情况
针对上述方法编写了3个Xunit单元测试:
public async Task CreateFileShare_ShouldThrowException_WhenShareAlreadyExists() public async Task CreateFileShare_ShouldThrowException_WhenInvalidShareName() public async Task CreateFileShare_ShouldCreateShare_WhenShareDoesNotExist()
前两个测试已通过,但第三个测试始终失败,以下是该测试的两种实现:
第一种实现
[Fact] public async Task CreateFileShare_ShouldCreateShare_WhenShareDoesNotExist() { // Arrange var responseMock = new Mock<Response<bool>>(); string validShareName = "valid-share-name"; responseMock.Setup(x => x.Value).Returns(false); _shareClientMock.Setup(x => x.ExistsAsync(It.IsAny<CancellationToken>())).Returns(Task.FromResult(responseMock.Object)); _shareServiceClientMock.Setup(x => x.GetShareClient(validShareName)).Returns(_shareClientMock.Object); // Act var createdShareClient = await _fileStorageHelper.CreateFileShare(validShareName); // Assert createdShareClient.Should().Be(_shareClientMock.Object); _shareClientMock.Verify(x => x.CreateAsync(It.IsAny<ShareCreateOptions>(), It.IsAny<CancellationToken>()), Times.Once); }
第二种实现
[Fact] public async Task CreateFileShare_ShouldCreateShare_WhenShareDoesNotExist() { // Arrange string validShareName = "validsharename"; var responseMock = new Mock<Response<bool>>(); var shareInfoMock = new Mock<ShareInfo>(); var createResponseMock = new Mock<Response<ShareInfo>>(); ShareCreateOptions createOptions = new ShareCreateOptions { QuotaInGB = 100, }; shareInfoMock.DefaultValue = DefaultValue.Mock; responseMock.Setup(x => x.Value).Returns(false); createResponseMock.Setup(x => x.Value).Returns(shareInfoMock.Object); _shareClientMock.Setup(x => x.ExistsAsync(It.IsAny<CancellationToken>())).Returns(Task.FromResult(responseMock.Object)); _shareClientMock.Setup(x => x.CreateAsync(createOptions, It.IsAny<CancellationToken>())).Returns(Task.FromResult(createResponseMock.Object)); _shareServiceClientMock.Setup(x => x.GetShareClient(validShareName)).Returns(_shareClientMock.Object); // Act var createdShareClient = await _fileStorageHelper.CreateFileShare(validShareName); // Assert createdShareClient.Should().Be(_shareClientMock.Object); _shareClientMock.Verify(x => x.ExistsAsync(It.IsAny<CancellationToken>()), Times.Once()); _shareClientMock.Verify(x => x.CreateAsync(It.IsAny<ShareCreateOptions>(), It.IsAny<CancellationToken>()), Times.Once()); }
错误信息
Message: Moq.MockException : Expected invocation on the mock once, but was 0 times: x => x.CreateAsync(It.IsAny<ShareCreateOptions>(), It.IsAny<CancellationToken>()) Performed invocations: Mock<ShareClient:1> (x): ShareClient.ExistsAsync(CancellationToken) => Mock<Response<bool>:1> ShareClient.CreateAsync(null, null, CancellationToken) Mock<Response<bool>:1>: Response<bool>.Value
问题原因与修复方案
1. GetShareClient参数大小写不匹配
原方法中调用_shareServiceClient.GetShareClient(shareName.ToLowerInvariant()),会将传入的shareName转为小写后再调用接口,但测试代码中Setup时使用的是原始的validShareName(未转小写)。当validShareName包含大写字母时,Mock无法匹配该调用,会返回默认的ShareClient实例(而非预设的_shareClientMock),导致后续验证逻辑失效。
修复:将Setup中的参数改为小写版本:
_shareServiceClientMock.Setup(x => x.GetShareClient(validShareName.ToLowerInvariant())) .Returns(_shareClientMock.Object);
2. CreateAsync重载不匹配
原方法调用的是无参数的CreateAsync(),对应Azure SDK中的实际重载是三参数版本:
Task<Response<ShareInfo>> CreateAsync(ShareCreateOptions? options = null, ShareCreateHeaders? headers = null, CancellationToken cancellationToken = default);
无参数调用会默认传入null、null、CancellationToken.None。但测试代码中Verify的是两参数版本的CreateAsync(ShareCreateOptions, CancellationToken),Moq认为该重载从未被调用,因此抛出异常。
修复:调整Verify语句,匹配实际调用的三参数重载:
// 匹配任意参数的三参数重载 _shareClientMock.Verify(x => x.CreateAsync(It.IsAny<ShareCreateOptions>(), It.IsAny<ShareCreateHeaders>(), It.IsAny<CancellationToken>()), Times.Once); // 或者更精准匹配默认传入的null参数 _shareClientMock.Verify(x => x.CreateAsync(null, null, It.IsAny<CancellationToken>()), Times.Once);
同时,第二种测试实现中Setup的CreateAsync重载也需要改为三参数版本:
_shareClientMock.Setup(x => x.CreateAsync(null, null, It.IsAny<CancellationToken>())) .Returns(Task.FromResult(createResponseMock.Object));
内容的提问来源于stack exchange,提问作者Pamador
相关产品推荐
相关产品推荐

