首页 文章

如何为单元测试创建HttpContext?

提问于
浏览
3

我正在努力模拟我的单元测试所需的 HttpContext .

我已经使用 SessionManager 接口从我的Mvc控制器中抽象出会话控制,并使用一个名为 CookieSessionManager 的类实现了它 . ( early development stages ) .

CookieSessionManager 使用注入的singleton HttpContextAccessor (在Startup.cs ConfigureServices中)使用 HttpContext .

我正在使用Cookie认证,它在Startup.cs中设置 app.UseCookieAuthentication .

Testing this manually in debug mode works as expected

我为 AccountController 类编写的 MSUnit 测试工作时注入了 MockSessionManager 类 .

我遇到的真正问题是我为 CookieSessionManager 课程编写的单元测试 . 我试图设置 HttpContext ,如下所示;

[TestClass]
public class CookieSessionManagerTest
{
    private IHttpContextAccessor contextAccessor;
    private HttpContext context;
    private SessionManager sessionManager;

    [TestInitialize]
    public void Setup_CookieSessionManagerTest()
    {
        context = new DefaultHttpContext();

        contextAccessor = new HttpContextAccessor();

        contextAccessor.HttpContext = context;

        sessionManager = new CookieSessionManager(contextAccessor);
    }

The Error

但是对 sessionManager.Login(CreateValidApplicationUser()); 的调用似乎没有设置 IsAuthenticated 标志,测试 CookieSessionManager_Login_ValidUser_Authenticated_isTrue 失败 .

[TestMethod]
public void CookieSessionManager_Login_ValidUser_Authenticated_isTrue()
{
    sessionManager.Login(CreateValidApplicationUser());

    Assert.IsTrue(sessionManager.isAuthenticated());
}

public ApplicationUser CreateValidApplicationUser()
{
    ApplicationUser applicationUser = new ApplicationUser();

    applicationUser.UserName = "ValidUser";

    //applicationUser.Password = "ValidPass";

    return applicationUser;
}

测试名称:CookieSessionManager_Login_ValidUser_Authenticated_isTrue:第43行测试结果:失败测试持续时间:0:00:00.0433169结果StackTrace:at ClaimsWebAppTests.Identity.CookieSessionManagerTest.CookieSessionManager_Login_ValidUser_Authenticated_isTrue()CookieSessionManagerTest.cs:第46行结果消息:Assert.IsTrue失败 .

MY CODE

SessionManager

using ClaimsWebApp.Models;

namespace ClaimsWebApp.Identity
{
    public interface SessionManager
    {
        bool isAuthenticated();

        void Login(ApplicationUser applicationUser);

        void Logout();
    }
}

CookieSessionManager

using ClaimsWebApp.Identity;
using ClaimsWebApp.Models;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Security.Claims;

namespace ClaimsWebApp
{
    public class CookieSessionManager : SessionManager
    {
        private List<ApplicationUser> applicationUsers;
        private IHttpContextAccessor ContextAccessor;
        private bool IsAuthenticated;

        public CookieSessionManager(IHttpContextAccessor contextAccessor)
        {
            this.IsAuthenticated = false;

            this.ContextAccessor = contextAccessor;

            IsAuthenticated = ContextAccessor.HttpContext.User.Identity.IsAuthenticated;

            applicationUsers = new List<ApplicationUser>();

            applicationUsers.Add(new ApplicationUser { UserName = "ValidUser" });
        }
        public bool isAuthenticated()
        {
            return IsAuthenticated;
        }

        public void Login(ApplicationUser applicationUser)
        {
            if (applicationUsers.Find(m => m.UserName.Equals(applicationUser.UserName)) != null)
            {
                var identity = new ClaimsIdentity(new[] {
                new Claim(ClaimTypes.Name, applicationUser.UserName)
                },
                "MyCookieMiddlewareInstance");

                var principal = new ClaimsPrincipal(identity);

                ContextAccessor.HttpContext.Authentication.SignInAsync("MyCookieMiddlewareInstance", principal);

                IsAuthenticated = ContextAccessor.HttpContext.User.Identity.IsAuthenticated;
            }
            else
            {
                throw new Exception("User not found");
            }
        }

        public void Logout()
        {
            ContextAccessor.HttpContext.Authentication.SignOutAsync("MyCookieMiddlewareInstance");

            IsAuthenticated = ContextAccessor.HttpContext.User.Identity.IsAuthenticated;
        }
    }
}

Startup.cs

using ClaimsWebApp.Identity;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace ClaimsWebApp
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddScoped<SessionManager, CookieSessionManager>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                AuthenticationScheme = "MyCookieMiddlewareInstance",
                LoginPath = new PathString("/Account/Unauthorized/"),
                AccessDeniedPath = new PathString("/Account/Forbidden/"),
                AutomaticAuthenticate = true,
                AutomaticChallenge = true
            });

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Account}/{action=Login}/{id?}");
            });
        }
    }
}

CookieSessionManagerTest.cs

using ClaimsWebApp;
using ClaimsWebApp.Identity;
using ClaimsWebApp.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ClaimsWebAppTests.Identity
{
    [TestClass]
    public class CookieSessionManagerTest
    {
        private IHttpContextAccessor contextAccessor;
        private HttpContext context;
        private SessionManager sessionManager;

        [TestInitialize]
        public void Setup_CookieSessionManagerTest()
        {
            context = new DefaultHttpContext();

            contextAccessor = new HttpContextAccessor();

            contextAccessor.HttpContext = context;

            sessionManager = new CookieSessionManager(contextAccessor);
        }

        [TestMethod]
        public void CookieSessionManager_Can_Be_Implemented()
        {
            Assert.IsInstanceOfType(sessionManager, typeof(SessionManager));
        }


        [TestMethod]
        public void CookieSessionManager_Default_Authenticated_isFalse()
        {
            Assert.IsFalse(sessionManager.isAuthenticated());
        }

        [TestMethod]
        public void CookieSessionManager_Login_ValidUser_Authenticated_isTrue()
        {
            sessionManager.Login(CreateValidApplicationUser());

            Assert.IsTrue(sessionManager.isAuthenticated());
        }

        public ApplicationUser CreateValidApplicationUser()
        {
            ApplicationUser applicationUser = new ApplicationUser();

            applicationUser.UserName = "ValidUser";

            //applicationUser.Password = "ValidPass";

            return applicationUser;
        }

        public ApplicationUser CreateInValidApplicationUser()
        {
            ApplicationUser applicationUser = new ApplicationUser();

            applicationUser.UserName = "InValidUser";

            //applicationUser.Password = "ValidPass";

            return applicationUser;
        }
    }
}

2 回答

  • 2

    不幸的是,用 HttpContext 测试几乎是不可能的 . 它's a sealed class that doesn' t利用任何接口,所以你不能嘲笑它 . 通常,最好的办法是抽象出与 HttpContext 一起使用的代码,然后测试其他更多特定于应用程序的代码 .

    看起来你已经通过 HttpContextAccessor 已经完成了这个,但你're utilizing it incorrectly. First, you'重新暴露 HttpContext 实例,这几乎完全违背了整个目的 . 这个类应该能够自己返回 User.Identity.IsAuthenticated 之类的内容,例如: httpContextAccessor.IsAuthenticated . 在内部,属性将访问私有 HttpContext 实例并返回结果 .

    一旦你以这种方式使用它,你就可以模拟 HttpContextAccessor 来简单地返回测试所需的内容,而你不必担心为它提供 HttpContext 实例 .

    当然,这意味着仍然有一些未经测试的代码,即与 HttpContext 一起使用的访问器方法,但这些代码通常非常简单 . 例如, IsAuthenticated 的代码就像 return httpContext.User.Identity.IsAuthenticated . 你要搞砸的唯一方法就是如果你发脾气,但编译器会警告你 .

  • 6

    这并没有直接回答问题的背景,但它提供了另一种测试方法,当你开始使用它时,生活变得更加容易 .

    有一个可用于ASP.NET Core的集成测试包,有关它的文档可以在这里找到:

    https://docs.asp.net/en/latest/testing/integration-testing.html

    请享用!

相关问题