[英]C# Integration Tests - How Do you Set The Auth Cookie?
我的 C# MVC 應用程序中有以下 AdminController 測試。 該文件看起來像這樣。
AdminControllerTests.cs:
using System.Collections.Generic;
using System.Net;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.Net.Http.Headers;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using AngleSharp.Html.Dom;
using Xunit;
using amaranth.Helpers;
using amaranth.Models;
using amaranth.Data;
using Microsoft.AspNetCore.Identity;
namespace amaranth.Tests
{
public class AdminControllerTests :
IClassFixture<CustomWebApplicationFactory<amaranth.Startup>>
{
private readonly CustomWebApplicationFactory<amaranth.Startup>
_factory;
public AdminControllerTests(
CustomWebApplicationFactory<amaranth.Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task Post_FirsttoRegisterClaimsAdmin()
{
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = true
});
var initResponse = await client.GetAsync("/Identity/Account/Register");
var antiForgeryValues = await AntiForgeryTokenExtractor.ExtractAntiForgeryValues(initResponse);
var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Identity/Account/Register");
postRequest.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
var formModel = new Dictionary<string, string>
{
{ AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
{ "Email", "test@example.com" },
{ "Password", "pas3w0!rRd" },
{ "ConfirmPassword", "pas3w0!rRd" },
};
postRequest.Content = new FormUrlEncodedContent(formModel);
var response = await client.SendAsync(postRequest);
response.EnsureSuccessStatusCode();
var postRequest2 = new HttpRequestMessage(HttpMethod.Post, "/Home/SetAdminToUser");
postRequest2.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
var formModel2 = new Dictionary<string, string>
{
{ AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
{ "check1", "True" },
{ "check2", "True" },
{ "check3", "True" },
};
postRequest2.Content = new FormUrlEncodedContent(formModel2);
var response2 = await client.SendAsync(postRequest2);
response2.EnsureSuccessStatusCode();
Assert.Equal("http://localhost/Home/ClaimAdmin", response.RequestMessage.RequestUri.ToString());
Assert.Equal("http://localhost/Admin/MasterWalletList", response2.RequestMessage.RequestUri.ToString());
IdentityUserRole<string>? userRl;
IdentityUser? user;
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
user = db.Users.FirstOrDefault();
userRl = db.UserRoles.FirstOrDefault();
}
Assert.NotNull(user);
Assert.True(userRl?.UserId == user?.Id);
}
[Fact]
public async Task CreateMasterWalletTest()
{
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = true
});
var initResponse = await client.GetAsync("/Admin/MasterWalletList");
var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Admin/CreateMasterWallet");
var antiForgeryValues = await AntiForgeryTokenExtractor.ExtractAntiForgeryValues(initResponse);
postRequest.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
var formModel = new Dictionary<string, string>
{
{ AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
{ "label", "testWallet1t" },
{ "isTestNet", "True" }
};
postRequest.Content = new FormUrlEncodedContent(formModel);
var response = await client.SendAsync(postRequest);
response.EnsureSuccessStatusCode();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
IdentityUserRole<string>? userRl;
MasterWallet? wallet1;
IdentityUser user;
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
wallet1 = db.MasterWallets.FirstOrDefault();
userRl = db.UserRoles.FirstOrDefault();
user = db.Users.FirstOrDefault();
}
Assert.Equal(user.Email, "test@example.com");
//Assert.Equal(userRl?.UserId, user?.Id);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(wallet1);
Assert.Equal(wallet1?.Address.Substring(0, 3), "tb1");
}
}
}
第二個測試CreateMasterWalletTest
失敗並出現以下錯誤:
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 6.0.5 initialized 'ApplicationDbContext' using provider 'Microsoft.EntityFrameworkCore.InMemory:6.0.5' with options: StoreName=InMemoryDbForTesting
[xUnit.net 00:00:02.01] amaranth.Tests.AdminControllerTests.CreateMasterWalletTest [FAIL]
Failed amaranth.Tests.AdminControllerTests.CreateMasterWalletTest [38 ms]
Error Message:
Assert.NotNull() Failure
Stack Trace:
at amaranth.Tests.AdminControllerTests.CreateMasterWalletTest() in /path/to/dir/amaranth.Tests/IntegrationTests/AdminControllerTests.cs:line 128
--- End of stack trace from previous location ---
Failed! - Failed: 1, Passed: 5, Skipped: 0, Total: 6, Duration: 2 s - /path/to/dir/amaranth.Tests/bin/Debug/net6.0/amaranth.Tests.dll (net6.0)
我可以看到問題是“/Admin/CreateMasterWallet”需要經過身份驗證的用戶(通過 cookie)。 我的調試器顯示當我調用var response = await client.SendAsync(postRequest);
, response
正嘗試重定向到登錄頁面,就像它對所有未經身份驗證的用戶所做的那樣。
那么如何讓我的用戶進行身份驗證並將身份驗證 cookie 發送到 Http 請求?
如果相關,這是我的 CustomWebApplicationFactory 文件。
CustomWebApplicationFactory.cs
using System;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using amaranth.Data;
namespace amaranth.Tests
{
public class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup: class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<ApplicationDbContext>));
services.Remove(descriptor);
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
services.AddAntiforgery(t =>
{
t.Cookie.Name = AntiForgeryTokenExtractor.AntiForgeryCookieName;
t.FormFieldName = AntiForgeryTokenExtractor.AntiForgeryFieldName;
});
var sp = services.BuildServiceProvider();
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
var logger = scopedServices
.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
db.Database.EnsureCreated();
}
});
}
}
}
此外,如果問題現在已經存在 2 年了,解決方案是像用戶一樣向客戶端調用登錄功能(也許您還必須處理 CSRF 保護)。 在服務器端,在簽名期間,Session Cookie 被添加到響應中,如下所述: https://docs.duendesoftware.com/identityserver/v6/ui/login/session/然后您可以使用相同的客戶端需要 Cookie 認證的請求。 然后可以使用[Authorize(AuthenticationSchemes = IdentityServerConstants.DefaultCookieAuthenticationScheme)]
或“CookieAuthenticationDefaults.AuthenticationScheme”保護 controller。 我使用這種解決方案為來自 IdentityServer 的AuthorizeInteractionResponseGenerator
提供資源,以強制用戶在被重定向到應用程序之前添加一些數據,例如他的地址或電話號碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.