如何在Xunit中Mock Azure DefaultAzureCredential适配DevOps流水线
解决方法
1. 重构原代码,通过依赖注入实现可测试性
原代码直接在方法内实例化DefaultAzureCredential,导致无法Mock。我们需要依赖注入TokenCredential接口(DefaultAzureCredential实现了该接口),让代码解耦:
重构后的业务代码
// 类的构造函数注入依赖 private readonly IConfiguration _configuration; private readonly ILogger _logger; private readonly TokenCredential _tokenCredential; public YourServiceClassName(IConfiguration configuration, ILogger logger, TokenCredential tokenCredential) { _configuration = configuration; _logger = logger; _tokenCredential = tokenCredential; } private async Task<AccessToken> AcquireManagementAccessTokenAsync(CancellationToken cancellationToken) { var scope = _configuration.GetValue<string>("Azure:ArmAuthScope"); bool userManagedIdentity; bool.TryParse(_configuration.GetValue<string>("Azure:UseManagedIdentity"), out userManagedIdentity); AccessToken token = new AccessToken(); if (userManagedIdentity) { try { // 使用注入的TokenCredential,而非直接实例化 token = await _tokenCredential.GetTokenAsync(new TokenRequestContext(new[] { scope }), cancellationToken); } catch (Exception ex) { _logger?.LogError(ex.Message); } } return token; }
2. 编写单元测试时Mock TokenCredential
使用Moq框架模拟TokenCredential的GetTokenAsync方法,返回预设的AccessToken,完全脱离Azure权限环境运行测试:
单元测试示例
using Moq; using Azure.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; [TestClass] public class YourServiceClassNameTests { [TestMethod] public async Task AcquireManagementAccessTokenAsync_ManagedIdentityEnabled_ReturnsMockedToken() { // 1. 模拟配置项 var mockConfig = new Mock<IConfiguration>(); mockConfig.Setup(c => c.GetValue<string>("Azure:ArmAuthScope")).Returns("https://management.azure.com/.default"); mockConfig.Setup(c => c.GetValue<string>("Azure:UseManagedIdentity")).Returns("true"); // 2. 模拟Logger var mockLogger = new Mock<ILogger<YourServiceClassName>>(); // 3. 模拟TokenCredential,返回预设令牌 var mockTokenCredential = new Mock<TokenCredential>(); var mockedToken = new AccessToken("fake-valid-token", DateTimeOffset.UtcNow.AddHours(1)); mockTokenCredential.Setup(tc => tc.GetTokenAsync( It.IsAny<TokenRequestContext>(), It.IsAny<CancellationToken>())) .ReturnsAsync(mockedToken); // 4. 实例化被测服务 var sut = new YourServiceClassName(mockConfig.Object, mockLogger.Object, mockTokenCredential.Object); // 5. 执行测试方法 var result = await sut.AcquireManagementAccessTokenAsync(CancellationToken.None); // 6. 验证结果 Assert.AreEqual("fake-valid-token", result.Token); Assert.IsTrue(result.ExpiresOn > DateTimeOffset.UtcNow); } [TestMethod] public async Task AcquireManagementAccessTokenAsync_ManagedIdentityDisabled_ReturnsEmptyToken() { // 模拟配置项 var mockConfig = new Mock<IConfiguration>(); mockConfig.Setup(c => c.GetValue<string>("Azure:UseManagedIdentity")).Returns("false"); var mockLogger = new Mock<ILogger<YourServiceClassName>>(); var mockTokenCredential = new Mock<TokenCredential>(); var sut = new YourServiceClassName(mockConfig.Object, mockLogger.Object, mockTokenCredential.Object); var result = await sut.AcquireManagementAccessTokenAsync(CancellationToken.None); Assert.IsEmpty(result.Token); } }
3. 生产环境注入真实凭据
在Program.cs(.NET 6+)中注册DefaultAzureCredential作为TokenCredential的实现,保证生产环境正常使用真实权限:
Program.cs配置示例
builder.Services.AddSingleton<TokenCredential>(_ => new DefaultAzureCredential());
修改后,单元测试可完全脱离Azure环境执行,DevOps流水线中的测试不再因权限问题失败。
内容的提问来源于stack exchange,提问作者VMG
相关产品推荐
相关产品推荐

