簡體   English   中英

C# 如何在 MVC 應用程序的集成測試中注銷?

[英]C# How Do I Logout in An Integration Test for An MVC App?

我有一個 MVC 應用程序,我正在為其編寫集成測試。 我有一個我正在測試的過程,涉及用戶注銷然后登錄。我可以登錄就好了。 下面的測試成功(順便說一句,我在 memory db 中注入了一個匹配的用戶條目):

[Fact]
public async Task D_LoginTest()
{
    var client = _factory.CreateClient(
        new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = true
        });
    var initResponse = await client.GetAsync("/Identity/Account/Login");
    var antiForgeryValues = await AntiForgeryTokenExtractor.ExtractAntiForgeryValues(initResponse);

    var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Identity/Account/Login");
    postRequest.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
    var formModel = new Dictionary<string, string>
    {
        { AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
        { "Input.Email", "test@example.com" },
        { "Input.Password", "pas3w0!rRd" }
    };
    postRequest.Content = new FormUrlEncodedContent(formModel);
    var response = await client.SendAsync(postRequest);
    response.EnsureSuccessStatusCode();
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

但是當我在這個測試中添加注銷時:

[Fact]
public async Task D_LoginTest()
{
    var client = _factory.CreateClient(
        new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = true
        });
    var initResponse = await client.GetAsync("/Identity/Account/Login");
    var antiForgeryValues = await AntiForgeryTokenExtractor.ExtractAntiForgeryValues(initResponse);

    var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Identity/Account/Login");
    postRequest.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
    var formModel = new Dictionary<string, string>
    {
        { AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
        { "Input.Email", "test@example.com" },
        { "Input.Password", "pas3w0!rRd" }
    };
    postRequest.Content = new FormUrlEncodedContent(formModel);
    var response = await client.SendAsync(postRequest);
    response.EnsureSuccessStatusCode();
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);

    var postRequestLogout = new HttpRequestMessage(HttpMethod.Post, "/Identity/Account/Logout");
    postRequestLogout.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
    var postRequestLougoutForm = new Dictionary<string, string>
    {
        { AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
    };
    postRequestLogout.Content = new FormUrlEncodedContent(postRequestLougoutForm);
    var logoutAnswer = await client.SendAsync(postRequestLogout);
    logoutAnswer.EnsureSuccessStatusCode();
    Console.WriteLine(logoutAnswer.StatusCode);
    Assert.Equal(HttpStatusCode.OK, logoutAnswer.StatusCode);
}

它失敗並出現此錯誤。

Failed amaranth.Tests.AdminControllerTests.D_LoginTest [23 ms]
  Error Message:
   System.Net.Http.HttpRequestException : Response status code does not indicate success: 400 (Bad Request).
  Stack Trace:
     at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
   at amaranth.Tests.AdminControllerTests.D_LoginTest() in /path/to/project/dir/amaranth.Tests/IntegrationTests/AdminControllerTests.cs:line 308
--- End of stack trace from previous location ---

另外,如果它有幫助,這是 Logout.cshtml.cs 文件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace amaranth.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return Page();
            }
        }
    }
}

這是我的調試器中的錯誤圖片:
在此處輸入圖像描述

我究竟做錯了什么? 如何在集成測試中注銷? 順便說一句,如果有幫助,這是默認的 Razor Page MVC 腳手架注銷。

我創建了一個最小的可重現示例amaranth-minal-testing-example ,它在集成測試中成功注銷。 首先這是amaranth-minal-testing-example/TestRunProject.Tests/AntiForgeryTokenExtractor.cs

using System.Text.RegularExpressions;
using Microsoft.Net.Http.Headers;

namespace TestRunProject.Tests
{
    public static class AntiForgeryTokenExtractor
    {
        public static string ExtractAntiForgeryToken(string htmlBody)
        {
            var requestVerificationTokenMatch =
                Regex.Match(htmlBody, $@"\<input name=""__RequestVerificationToken"" type=""hidden"" value=""([^""]+)"" \/\>");

            if (requestVerificationTokenMatch.Success)
                return requestVerificationTokenMatch.Groups[1].Captures[0].Value;

            throw new ArgumentException($"Anti forgery token '__RequestVerificationToken' not found in HTML", nameof(htmlBody));
        }
    }
}

這是amaranth-minal-testing-example/TestRunProject.Tests/IntegrationTests/AuthTest.cs

using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Net.Http.Headers;
using Microsoft.VisualStudio.TestPlatform.TestHost;

namespace TestRunProject.Tests
{
    public class AuthTests
    {
        [Fact]
        public async Task DLoginTest()
        {
            {
                var application = new WebApplicationFactory<Program>()
                    .WithWebHostBuilder(builder =>
                    {
                        // ... Configure test services
                    });

                var client = application.CreateClient();

                // -- REGISTER --

                var registerResponse = await client.GetAsync("/Identity/Account/Register");
                registerResponse.EnsureSuccessStatusCode();
                string registerResponseContent = await registerResponse.Content.ReadAsStringAsync();

                var requestVerificationToken = AntiForgeryTokenExtractor.ExtractAntiForgeryToken(registerResponseContent);
                
                var formModel = new Dictionary<string, string>
                {
                    { "Input.Email", "test2@example.com" },
                    { "Input.Password", "pas3w02!rRd" },
                    { "Input.ConfirmPassword", "pas3w02!rRd" },
                    { "__RequestVerificationToken", requestVerificationToken },
                };

                var postRequest2 = new HttpRequestMessage(HttpMethod.Post, "/Identity/Account/Register");
                postRequest2.Content = new FormUrlEncodedContent(formModel);
                var registerResponse2 = await client.SendAsync(postRequest2);
                registerResponse2.EnsureSuccessStatusCode();

                // -- LOGOUT --

                var logoutRequest = new StringContent("");
                logoutRequest.Headers.Add("RequestVerificationToken", requestVerificationToken);

                var logoutResponse = await client.PostAsync("/Identity/Account/Logout", logoutRequest);

                logoutResponse.EnsureSuccessStatusCode();

                // -- LOGIN --

                var loginResponse = await client.GetAsync("/Identity/Account/Login");
                loginResponse.EnsureSuccessStatusCode();
                string loginResponseContent = await registerResponse.Content.ReadAsStringAsync();

                requestVerificationToken = AntiForgeryTokenExtractor.ExtractAntiForgeryToken(loginResponseContent);

                formModel = new Dictionary<string, string>
                {
                    { "Input.Email", "test2@example.com" },
                    { "Input.Password", "pas3w02!rRd" },
                    { "__RequestVerificationToken", requestVerificationToken },
                };

                var loginRequest = new HttpRequestMessage(HttpMethod.Post, "/Identity/Account/Login");
                loginRequest.Content = new FormUrlEncodedContent(formModel);
                var loginResponse2 = await client.SendAsync(loginRequest);
                loginResponse2.EnsureSuccessStatusCode();
            }
        }
    }
}

This works for a default scaffolded C# ASP.NET core identity razor pages project when TestRunProject.Tests is placed in the same directory as TestRunProject (the default scaffolded C# ASP.NET core identity razor pages project).

我在https://github.com/ChristianOConnor/amaranth-minal-testing-example有此代碼的 Github 存儲庫。 上面的代碼來自這個提交: 0d768c4aa181cb4289de4b17f1eac222323ee469

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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