我正在使用Visual Studio 2015 Enterprise Update 1和ASP.NET 5 rc1-final构建一个端点,该端点同时发布和使用JWT令牌,详细信息如here所述。
现在,在测试AspNet.Security.OpenIdConnect.Server(OIDC / ASOS)的某些方面时,我正在进行单元测试并遇到摩擦。具体来说,一些ASOS原语(如LogoutEndpointContext)不是抽象的,因此不容易模拟它们。通常情况下,我只会在这些类型上抛出fake,但DNX似乎不支持假货,至少现在还没有。这些类型也是密封的,所以我不能把它们专门化。
这迫使我编写一些脆弱的反射代码来构造这些密封的ASOS类型。这是一个需要LogoutEndpointContext的示例XUnit测试,因此我可以测试我的OpenIdConnectServerProvider事件处理(在这种情况下,非POST注销应该抛出异常);请注意我必须做的反射才能实例化LogoutEndpointContext:
[Fact]
async public Task API_Initialization_Services_AuthenticatedUser_Authentication_LogoutEndpoint_XSRF_Unit()
{
// Arrange
Mock<HttpRequest> mockRequest = new Mock<HttpRequest>();
mockRequest.SetupGet(a => a.Method).Returns(() => "Not Post");
Mock<HttpContext> mockContext = new Mock<HttpContext>();
mockContext.SetupGet(a => a.Request).Returns(() => mockRequest.Object);
OpenIdConnectServerOptions options = new OpenIdConnectServerOptions();
OpenIdConnectMessage request = new OpenIdConnectMessage();
// I would prefer not to use reflection
var ctorInfo = typeof(LogoutEndpointContext).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).Single();
LogoutEndpointContext context = (LogoutEndpointContext)ctorInfo.Invoke(new object[] {mockContext.Object,options, request});
// Act
AuthenticationEvents authenticationEvents = new AuthenticationEvents();
// Assert
await Assert.ThrowsAsync<SecurityTokenValidationException>(() => authenticationEvents.LogoutEndpoint(context));
}
关于如何更好地实例化/模拟/伪造/专门化密封的ASOS类型(如LogoutEndpointContext)的任何建议都将非常感激。
答案 0 :(得分:0)
在测试严重依赖于HTTP上下文的中间件时,单元测试很少是最简单的选择。我们推荐的方法是通过使用TestServer
来设置内存管道来选择功能测试:
public class OAuthLogoutEndpointTests {
[Fact]
public async Task EnsureThatOnlyPostLogoutRequestsAreValid() {
// Arrange
var server = CreateAuthorizationServer();
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "/connect/logout");
// Act
var response = await client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
private static TestServer CreateAuthorizationServer(Action<OpenIdConnectServerOptions> configuration = null) {
var builder = new WebHostBuilder();
builder.UseEnvironment("Testing");
builder.ConfigureServices(services => {
services.AddAuthentication();
services.AddCaching();
services.AddLogging();
});
builder.Configure(app => {
// Add a new OpenID Connect server instance.
app.UseOpenIdConnectServer(options => {
// Use your own provider:
// options.Provider = new AuthenticationEvents();
options.Provider = new OpenIdConnectServerProvider {
OnValidateLogoutRequest = context => {
// Reject non-POST logout requests.
if (!string.Equals(context.HttpContext.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Only POST requests are supported.");
}
return Task.FromResult(0);
}
};
// Run the configuration delegate
// registered by the unit tests.
configuration?.Invoke(options);
});
});
return new TestServer(builder);
}
}
编辑:此答案的其余部分不再适用,因为ASOS beta5中已启封了上下文类。
在ASOS中,所有上下文类都已被密封,其构造函数被标记为内部,以防止开发人员实例化它们并在生产代码中手动调用提供程序。也就是说,如果您认为我们不应该这样做,请随意在GitHub存储库上打开新票。
最后一句话:如果您希望您的OIDC服务器符合规范,拒绝GET请求可能不是一个好主意,因为它是规范中明确提到的唯一动词。