我正在寻找一些指导...
我目前正在尝试为.net core 2.1中的Razor Pages应用编写一些集成测试,我想测试的页面是后期身份验证,但是我不确定实现该方法的最佳方法。该文档似乎建议创建一个CustomWebApplicationFactory,但除此之外,我还有些迷茫,因为我无法使用基于Cookie的基本身份验证来伪造/模拟已身份验证的用户/请求。
我已经看到针对Microsoft文档有一个开放的GitHub issue(这里是实际的GitHub issue),有一个提到的使用IdentityServer4的解决方案,但我只是在寻找如何执行此操作仅使用基于cookie的身份验证。
有人得到了他们可能会建议的指导吗?
预先感谢
到目前为止,我的代码是:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseMySql(connectionString);
options.EnableSensitiveDataLogging();
});
services.AddLogging(builder =>
{
builder.AddSeq();
});
services.ConfigureAuthentication();
services.ConfigureRouting();
}
}
ConfigureAuthentication.cs
namespace MyCarparks.Configuration.Startup
{
public static partial class ConfigurationExtensions
{
public static IServiceCollection ConfigureAuthentication(this IServiceCollection services)
{
services.AddIdentity<MyCarparksUser, IdentityRole>(cfg =>
{
//cfg.SignIn.RequireConfirmedEmail = true;
})
.AddDefaultUI()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = $"/Identity/Account/Login";
options.LogoutPath = $"/Identity/Account/Logout";
options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
});
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddRazorPagesOptions(options =>
{
options.AllowAreas = true;
options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
options.Conventions.AuthorizeFolder("/Sites");
});
return services;
}
}
}
集成测试
PageTests.cs
namespace MyCarparks.Web.IntegrationTests
{
public class PageTests : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly CustomWebApplicationFactory<Startup> factory;
public PageTests(CustomWebApplicationFactory<Startup> webApplicationFactory)
{
factory = webApplicationFactory;
}
[Fact]
public async Task SitesReturnsSuccessAndCorrectContentTypeAndSummary()
{
var siteId = Guid.NewGuid();
var site = new Site { Id = siteId, Address = "Test site address" };
var mockSite = new Mock<ISitesRepository>();
mockSite.Setup(s => s.GetSiteById(It.IsAny<Guid>())).ReturnsAsync(site);
// Arrange
var client = factory.CreateClient();
// Act
var response = await client.GetAsync("http://localhost:44318/sites/sitedetails?siteId=" + siteId);
// Assert
response.EnsureSuccessStatusCode();
response.Content.Headers.ContentType.ToString()
.Should().Be("text/html; charset=utf-8");
var responseString = await response.Content.ReadAsStringAsync();
responseString.Should().Contain("Site Details - MyCarparks");
}
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseStartup<Startup>();
}
}
}
答案 0 :(得分:0)
要实现您的要求,您可以尝试下面的代码,该代码使用身份验证cookie创建客户端。
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint> where TEntryPoint : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
});
base.ConfigureWebHost(builder);
}
public new HttpClient CreateClient()
{
var cookieContainer = new CookieContainer();
var uri = new Uri("https://localhost:44344/Identity/Account/Login");
var httpClientHandler = new HttpClientHandler
{
CookieContainer = cookieContainer
};
HttpClient httpClient = new HttpClient(httpClientHandler);
var verificationToken = GetVerificationToken(httpClient, "https://localhost:44344/Identity/Account/Login");
var contentToSend = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("Email", "test@outlook.com"),
new KeyValuePair<string, string>("Password", "1qaz@WSX"),
new KeyValuePair<string, string>("__RequestVerificationToken", verificationToken),
});
var response = httpClient.PostAsync("https://localhost:44344/Identity/Account/Login", contentToSend).Result;
var cookies = cookieContainer.GetCookies(new Uri("https://localhost:44344/Identity/Account/Login"));
cookieContainer.Add(cookies);
var client = new HttpClient(httpClientHandler);
return client;
}
private string GetVerificationToken(HttpClient client, string url)
{
HttpResponseMessage response = client.GetAsync(url).Result;
var verificationToken =response.Content.ReadAsStringAsync().Result;
if (verificationToken != null && verificationToken.Length > 0)
{
verificationToken = verificationToken.Substring(verificationToken.IndexOf("__RequestVerificationToken"));
verificationToken = verificationToken.Substring(verificationToken.IndexOf("value=\"") + 7);
verificationToken = verificationToken.Substring(0, verificationToken.IndexOf("\""));
}
return verificationToken;
}
}
答案 1 :(得分:0)
遵循Chris Pratt的建议,但对于Razor Pages .NET Core 3.1,我使用先前的请求针对登录端点(这也是另一个razor页面)进行身份验证,并从响应中获取cookie。然后,我将相同的cookie作为http请求的一部分添加,瞧,这是经过身份验证的请求。
这是一段使用HttpClient and AngleSharp(作为正式的Microsoft文档)来测试剃须刀页面的代码。因此,我重新使用它来从响应中获取Cookie。
private async Task<string> GetAuthenticationCookie()
{
var formName = nameof(LoginModel.LoginForm); //this is the bounded model for the login page
var dto =
new Dictionary<string, string>
{
[$"{formName}.Username"] = "foo",
[$"{formName}.Password"] = "bar",
};
var page = HttpClient.GetAsync("/login").GetAwaiter().GetResult();
var content = HtmlHelpers.GetDocumentAsync(page).GetAwaiter().GetResult();
//this is the AndleSharp
var authResult =
await HttpClient
.SendAsync(
(IHtmlFormElement)content.QuerySelector("form[id='login-form']"),
(IHtmlButtonElement)content.QuerySelector("form[id='login-form']")
.QuerySelector("button"),
dto);
_ = authResult.Headers.TryGetValues("Set-Cookie", out var values);
return values.First();
}
然后可以重复使用该值,并将其与新的http请求一起传递。
//in my test, the cookie is a given (from the given-when-then approach) pre-requirement
protected void Given()
{
var cookie = GetAuthenticationCookie().GetAwaiter().GetResult();
//The http client is a property that comes from the TestServer, when creating a client http for tests as usual. Only this time I set the auth cookie to it
HttpClient.DefaultRequestHeaders.Add("Set-Cookie", cookie);
var page = await HttpClient.GetAsync($"/admin/protectedPage");
//this will be a 200 OK because it's an authenticated request with whatever claims and identity the /login page applied
}