gpt4 book ai didi

c# - 单元测试自定义 AuthenticationHandler 中间件

转载 作者:行者123 更新时间:2023-12-03 16:29:55 24 4
gpt4 key购买 nike

你如何对继承自 AuthenticationHandler<AuthenticationSchemeOptions> 的自定义中间件进行单元测试?

我从它继承的自定义类用于基本身份验证。

    public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IProvidePrincipal _principalProvider;

public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IProvidePrincipal principalProvider)
: base(options, logger, encoder, clock)
{
_principalProvider = principalProvider;
}

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (Request.Headers.TryGetValue(HeaderNames.Authorization, out StringValues authorizationHeader))
{
if (Credentials.TryParse(authorizationHeader, out Credentials credentials))
{
var principal = await _principalProvider.GetClaimsPrincipalAsync(credentials.Username, credentials.Password, Scheme.Name);

if (principal != null)
{
var ticket = new AuthenticationTicket(principal, Scheme.Name);

return AuthenticateResult.Success(ticket);
}
else
{
return AuthenticateResult.Fail("Basic authentication failed. Invalid username and password.");
}
}
else
{
return AuthenticateResult.Fail("Basic authentication failed. Unable to parse username and password.");
}
}

return AuthenticateResult.Fail("Basic authentication failed. Authorization header is missing.");
}
}

最佳答案

单元测试自定义中间件相对容易,但是当你从 AuthenticationHandler 继承时,基类在其中抛出了一个 Spanner 。在到处寻找并且只找到集成测试之后,我终于能够弄清楚如何去做。
单元测试的基本设置不会每次测试都改变。

    [TestClass]
public class BasicAuthenticationTests
{
private readonly Mock<IOptionsMonitor<AuthenticationSchemeOptions>> _options;
private readonly Mock<ILoggerFactory> _loggerFactory;
private readonly Mock<UrlEncoder> _encoder;
private readonly Mock<ISystemClock> _clock;
private readonly Mock<IProvidePrincipal> _principalProvider;
private readonly BasicAuthenticationHandler _handler;

public BasicAuthenticationTests()
{
_options = new Mock<IOptionsMonitor<AuthenticationSchemeOptions>>();

// This Setup is required for .NET Core 3.1 onwards.
_options
.Setup(x => x.Get(It.IsAny<string>()))
.Returns(new AuthenticationSchemeOptions());

var logger = new Mock<ILogger<BasicAuthenticationHandler>>();
_loggerFactory = new Mock<ILoggerFactory>();
_loggerFactory.Setup(x => x.CreateLogger(It.IsAny<String>())).Returns(logger.Object);

_encoder = new Mock<UrlEncoder>();
_clock = new Mock<ISystemClock>();
_principalProvider = new Mock<IProvidePrincipal>();

_handler = new BasicAuthenticationHandler(_options.Object, _loggerFactory.Object, _encoder.Object, _clock.Object, _principalProvider.Object);
}
特别说明 _loggerFactory.Setup(x => x.CreateLogger(It.IsAny<String>())).Returns(logger.Object);如果您不这样做,则在您的处理程序完成您无法调试的代码中的空引用后,您的单元测试将爆炸。这是因为基类调用了 CreateLogger在其构造函数中。
现在,您可以使用 DefaultHttpContext 设置上下文来测试逻辑。
        [TestMethod]
public async Task HandleAuthenticateAsync_NoAuthorizationHeader_ReturnsAuthenticateResultFail()
{
var context = new DefaultHttpContext();

await _handler.InitializeAsync(new AuthenticationScheme(BasicAuthenticationHandler.SchemeName, null, typeof(BasicAuthenticationHandler)), context);
var result = await _handler.AuthenticateAsync();

Assert.IsFalse(result.Succeeded);
Assert.AreEqual("Basic authentication failed. Authorization header is missing.", result.Failure.Message);
}
备注 你不能打电话 HandleAuthenticateAsync直接,因为它受到保护。处理程序必须先初始化然后调用 AuthenticateAsync .
我包括了下面要测试的其余逻辑,以举例说明如何操作上下文
并对不同测试场景的结果进行断言。
        [TestMethod]
public async Task HandleAuthenticateAsync_CredentialsTryParseFails_ReturnsAuthenticateResultFail()
{
var context = new DefaultHttpContext();
var authorizationHeader = new StringValues(String.Empty);
context.Request.Headers.Add(HeaderNames.Authorization, authorizationHeader);

await _handler.InitializeAsync(new AuthenticationScheme(BasicAuthenticationHandler.SchemeName, null, typeof(BasicAuthenticationHandler)), context);
var result = await _handler.AuthenticateAsync();

Assert.IsFalse(result.Succeeded);
Assert.AreEqual("Basic authentication failed. Unable to parse username and password.", result.Failure.Message);
}

[TestMethod]
public async Task HandleAuthenticateAsync_PrincipalIsNull_ReturnsAuthenticateResultFail()
{
_principalProvider.Setup(m => m.GetClaimsPrincipalAsync(It.IsAny<String>(), It.IsAny<String>(), It.IsAny<String>())).ReturnsAsync((ClaimsPrincipal)null);

var context = new DefaultHttpContext();
var authorizationHeader = new StringValues("Basic VGVzdFVzZXJOYW1lOlRlc3RQYXNzd29yZA==");
context.Request.Headers.Add(HeaderNames.Authorization, authorizationHeader);

await _handler.InitializeAsync(new AuthenticationScheme(BasicAuthenticationHandler.SchemeName, null, typeof(BasicAuthenticationHandler)), context);
var result = await _handler.AuthenticateAsync();

Assert.IsFalse(result.Succeeded);
Assert.AreEqual("Basic authentication failed. Invalid username and password.", result.Failure.Message);
}

[TestMethod]
public async Task HandleAuthenticateAsync_PrincipalIsNull_ReturnsAuthenticateResultSuccessWithPrincipalInTicket()
{
var username = "TestUserName";
var claims = new[] { new Claim(ClaimTypes.Name, username) };
var identity = new ClaimsIdentity(claims, BasicAuthenticationHandler.SchemeName);
var claimsPrincipal = new ClaimsPrincipal(identity);
_principalProvider.Setup(m => m.GetClaimsPrincipalAsync(It.IsAny<String>(), It.IsAny<String>(), It.IsAny<String>())).ReturnsAsync(claimsPrincipal);

var context = new DefaultHttpContext();
var authorizationHeader = new StringValues("Basic VGVzdFVzZXJOYW1lOlRlc3RQYXNzd29yZA==");
context.Request.Headers.Add(HeaderNames.Authorization, authorizationHeader);

await _handler.InitializeAsync(new AuthenticationScheme(BasicAuthenticationHandler.SchemeName, null, typeof(BasicAuthenticationHandler)), context);
var result = await _handler.AuthenticateAsync();

Assert.IsTrue(result.Succeeded);
Assert.AreEqual(BasicAuthenticationHandler.SchemeName, result.Ticket.AuthenticationScheme);
Assert.AreEqual(username, result.Ticket.Principal.Identity.Name);
}

关于c# - 单元测试自定义 AuthenticationHandler 中间件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58963133/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com