You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

C#单元测试中使用HttpContext.GetTokenAsync遇到的问题

解决HttpContext.GetTokenAsync扩展方法的单元测试模拟问题

这个错误的核心原因很明确:Moq无法直接模拟静态扩展方法HttpContext.GetTokenAsync本质是AuthenticationTokenExtensions类中的静态扩展方法,你不能直接对它做Setup。我们需要模拟它底层依赖的实例接口方法——也就是IAuthenticationService中对应的重载方法,同时修正你现有代码里的两个小问题:

修正后的模拟代码

首先要明确:HttpContext.GetTokenAsync("access_token")这个扩展方法的内部逻辑,其实是先获取默认的认证Scheme(通常是Bearer或你配置的默认值),然后调用IAuthenticationService.GetTokenAsync(HttpContext context, string scheme, string tokenName)这个三参数的重载。所以我们需要Setup这个正确的重载,而不是你之前用的双参数版本。另外,你的serviceProvider.Setup中返回的是Mock对象本身,而不是它的Object实例,这也是一个错误。

修正后的MakeFakeContext方法如下:

public static HttpContext MakeFakeContext() {
    var authServiceMock = new Mock<IAuthenticationService>();
    
    // Setup三参数的GetTokenAsync重载,匹配任意HttpContext、任意scheme、指定的tokenName
    authServiceMock.Setup(_ => _.GetTokenAsync(
        It.IsAny<HttpContext>(), 
        It.IsAny<string>(), // 匹配默认scheme
        "access_token" // 对应你要获取的令牌名称
    )).Returns(Task.FromResult("fake-token-123"));

    var serviceProviderMock = new Mock<IServiceProvider>();
    // 返回IAuthenticationService的实例(Mock的Object)
    serviceProviderMock.Setup(_ => _.GetService(typeof(IAuthenticationService)))
                       .Returns(authServiceMock.Object);

    return new DefaultHttpContext { 
        RequestServices = serviceProviderMock.Object 
    };
}

测试中的使用方式

和你之前的用法一致:

var mockContext = MakeFakeContext();
unitUnderTest.ControllerContext = new ControllerContext { 
    HttpContext = mockContext 
};

// 执行控制器方法,此时GetTokenAsync会返回我们设置的"fake-token-123"
var result = await unitUnderTest.YourActionMethod();

另一种更简洁的方案(无需Moq)

如果你的测试场景比较简单,也可以直接创建一个自定义的IAuthenticationService实现,不用Moq,代码更直观:

public class FakeAuthenticationService : IAuthenticationService
{
    public Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme)
    {
        // 如果控制器还用到认证逻辑,可在这里实现对应返回值
        return Task.FromResult(AuthenticateResult.NoResult());
    }

    public Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties)
    {
        return Task.CompletedTask;
    }

    public Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties)
    {
        return Task.CompletedTask;
    }

    public Task<string?> GetTokenAsync(HttpContext context, string scheme, string tokenName)
    {
        // 直接返回测试用令牌
        return Task.FromResult<string?>("fake-token-123");
    }
}

// 在MakeFakeContext中使用这个实现:
public static HttpContext MakeFakeContext() {
    var services = new ServiceCollection();
    services.AddSingleton<IAuthenticationService, FakeAuthenticationService>();
    
    return new DefaultHttpContext { 
        RequestServices = services.BuildServiceProvider() 
    };
}

这种方式避免了Moq的重载匹配问题,代码可读性更高,适合简单测试场景。


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

火山引擎 最新活动