簡體   English   中英

C# 集成測試 - 如何設置 Auth Cookie?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM