2016-10-27 47 views
3

我正在努力模拟我的单元测试所需的HttpContext如何为我的单元测试创​​建一个HttpContext?

我已经从我的Mvc控制器抽象控制了一个SessionManager接口,并通过一个名为CookieSessionManager的类实现了该控制。 (早期发展阶段)。 (在Startup.cs ConfigureServices中)使用HttpContext

我使用Cookie身份验证,它在Startup.cs中设置为app.UseCookieAuthentication

测试这个手动调试模式下按预期工作

我已经为我被注入了MockSessionManagerAccountController类工作写入的MSUnit测试。

我真正的问题是我为我的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); 
    } 

的错误

但调用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

结果堆栈跟踪:在ClaimsWebAppTests.Identity.CookieSessionManagerTest.CookieSessionManager_Login_ValidUser_Authenticated_isTrue()

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

我的代码

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

开始up.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; 
     } 
    } 
} 
+1

您是否使用TestServer进行过调查? https://docs.asp.net/zh/latest/testing/integration-testing.html – Brad

+0

谢谢@Brad,我想这正是我要找的。我只是在试图解决问题时努力做出单元测试。 –

+1

它可能嘲笑HttpContext与TypeMock隔离器,例如:https://www.typemock.com/docs?book=Isolator&page=Documentation%2FHtmlDocs%2Fsample1fakinghttpcontextandmodelstate.htm –

回答

6

不幸的是,这几乎是不可能的HttpContext测试。这是一个不使用任何接口的密封类,所以你不能嘲笑它。通常,您最好的办法是将与HttpContext一起使用的代码抽象出来,然后测试其他更多特定于应用程序的代码。

看起来你已经通过HttpContextAccessor完成了这项工作,但是你错误地利用了它。首先,您将暴露HttpContext实例,这实际上几乎无法实现整个目的。这个班级应该可以自行返回类似User.Identity.IsAuthenticated的内容,如:httpContextAccessor.IsAuthenticated。在内部,该属性将访问私有的HttpContext实例并返回结果。

一旦你以这种方式使用它,然后你可以模拟HttpContextAccessor来简单地返回你的测试需要的东西,而且你不必担心它提供了一个HttpContext实例。当然,这意味着还有一些未经测试的代码,即与HttpContext一起工作的访问器方法,但这些方法通常非常直截了当。例如,IsAuthenticated的代码只是return httpContext.User.Identity.IsAuthenticated。要解决这个问题的唯一方法就是如果你用手指点什么,但编译器会提醒你。

+0

感谢您的回答,我忙于消化它。 –

+0

那么认为测试Cookie身份验证中间件只能在正在运行的Web过程的上下文中作为集成测试的一部分来实现才是正确的;而且HttpContext交互的抽象是单元测试其他代码的关键,正如我认为你已经提出的那样? –

+0

是的。你的单元测试应该关注功能的小型离散“单元”,因此也就是名称。 cookie已被设置的事实是一个实现细节,对你的测试代码无关紧要。 –

相关问题