如何为单元测试创​​建HttpContext? [英] How do I create an HttpContext for my unit test?

查看:67
本文介绍了如何为单元测试创​​建HttpContext?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am struggling to simulate the required HttpContext for my unit tests.

我已经使用SessionManager接口从我的Mvc控制器中抽象出了对会话的控制,并使用了一个名为CookieSessionManager的类来实现该控制. (早期开发阶段).

I have abstracted control of the session away from my Mvc controller with a SessionManager interface and implemented this with a class called CookieSessionManager. (early development stages).

CookieSessionManager通过注入的单例HttpContextAccessor(在Startup.cs ConfigureServices中)使用HttpContext.

CookieSessionManager uses the HttpContext by using the injected singleton HttpContextAccessor (in Startup.cs ConfigureServices).

我正在使用Cookie身份验证,该身份验证是通过app.UseCookieAuthentication在Startup.cs中设置的.

I'm using Cookie Authentication which is setup in Startup.cs with app.UseCookieAuthentication.

在调试模式下手动测试可以正常工作

我为AccountController类编写的MSUnit测试工作是在注入MockSessionManager类的情况下进行的.

The MSUnit tests I have written for my AccountController class work with a MockSessionManager class injected.

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

The real issue I have is with the unit tests I have written for my CookieSessionManager class. I have tried to setup the HttpContext as shown below;

[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);
    }

错误

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

But the call to sessionManager.Login(CreateValidApplicationUser()); doesn't appear to set the IsAuthenticated flag and the test CookieSessionManager_Login_ValidUser_Authenticated_isTrue fails.

[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

Test Name: CookieSessionManager_Login_ValidUser_Authenticated_isTrue

:第43行测试结果:测试持续时间失败:0:00:00.0433169

: line 43 Test Outcome: Failed Test Duration: 0:00:00.0433169

结果StackTrace:在ClaimsWebAppTests.Identity.CookieSessionManagerTest.CookieSessionManager_Login_ValidUser_Authenticated_isTrue()

Result StackTrace: at ClaimsWebAppTests.Identity.CookieSessionManagerTest.CookieSessionManager_Login_ValidUser_Authenticated_isTrue()

CookieSessionManagerTest.cs:第46行结果消息:Assert.IsTrue失败.

CookieSessionManagerTest.cs:line 46 Result Message: Assert.IsTrue failed.

我的密码

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;
        }
    }
}

推荐答案

不幸的是,使用HttpContext进行测试几乎是不可能的.这是一个不使用任何接口的密封类,因此您无法对其进行模拟.通常,最好的选择是提取与HttpContext一起使用的代码,然后仅测试您的其他更特定于应用程序的代码.

Unfortunately, it's pretty much impossible to test with HttpContext. It's a sealed class that doesn't utilize any interfaces, so you cannot mock it. Usually, your best bet is to abstract away the code that works with HttpContext, and then test just your other, more application-specific code.

您似乎已经通过HttpContextAccessor完成了此操作,但是使用不正确.首先,您要公开HttpContext实例,该实例几乎破坏了整个目的.此类应能够自行返回类似User.Identity.IsAuthenticated的内容,例如:httpContextAccessor.IsAuthenticated.在内部,该属性将访问私有HttpContext实例并仅返回结果.

It looks like you've sort of already done this via HttpContextAccessor, but you're utilizing it incorrectly. First, you're exposing the HttpContext instance, which pretty much defeats the entire purpose. This class should be able to return something like User.Identity.IsAuthenticated on its own, like: httpContextAccessor.IsAuthenticated. Internally, the property would access the private HttpContext instance and just return the result.

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

Once you're utilizing it in this way, you can then mock HttpContextAccessor to simply return what you need for your tests, and you don't have to worry about supplying it with an HttpContext instance.

当然,这意味着仍有一些未经测试的代码,即与HttpContext一起使用的访问器方法,但这些方法通常非常简单.例如,IsAuthenticated的代码就是类似return httpContext.User.Identity.IsAuthenticated的代码.要解决这个问题的唯一方法是用胖手指,但是编译器会警告您.

Granted, this means there's still some untested code, namely, the accessor methods that work with HttpContext, but these are generally very straight-forward. For example, the code for IsAuthenticated would just be something like return httpContext.User.Identity.IsAuthenticated. The only way you're going to screw that up is if you fat-finger something, but the compiler will warn you about that.

这篇关于如何为单元测试创​​建HttpContext?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆