[英]How do I create an HttpContext for my unit test?
我正在努力为我的单元测试模拟所需的HttpContext
。
我已经使用SessionManager
接口从我的 Mvc 控制器中抽象出会话的控制,并使用一个名为CookieSessionManager
的类来实现它。 (早期发展阶段)。
CookieSessionManager
使用HttpContext
通过使用注射单HttpContextAccessor
(在Startup.cs ConfigureServices)。
我正在使用 Cookie 身份验证,它是在 Startup.cs 中通过app.UseCookieAuthentication
设置的。
在调试模式下手动测试它按预期工作
我为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);
}
错误
但是调用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:line 46 结果消息:Assert.IsTrue 失败。
我的代码
会话管理器
using ClaimsWebApp.Models;
namespace ClaimsWebApp.Identity
{
public interface SessionManager
{
bool isAuthenticated();
void Login(ApplicationUser applicationUser);
void Logout();
}
}
Cookie会话管理器
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;
}
}
}
启动文件
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
一起使用的代码,然后只测试其他更特定于应用程序的代码。
看起来您已经通过HttpContextAccessor
完成了此操作,但是您没有正确使用它。 首先,您暴露了HttpContext
实例,这几乎违背了整个目的。 此类应该能够自行返回类似User.Identity.IsAuthenticated
的内容,例如: httpContextAccessor.IsAuthenticated
。 在内部,该属性将访问私有HttpContext
实例并仅返回结果。
一旦以这种方式使用它,就可以模拟HttpContextAccessor
来简单地返回测试所需的内容,而不必担心为它提供HttpContext
实例。
当然,这意味着仍有一些未经测试的代码,即与HttpContext
一起使用的访问器方法,但这些通常非常简单。 例如, IsAuthenticated
的代码就像return httpContext.User.Identity.IsAuthenticated
。 你要搞砸的唯一方法是如果你用胖手指某些东西,但编译器会警告你。
我为我的单元测试创建了这个辅助功能,这允许我测试那些需要 httpRequest 部分的特定方法。
public static IHttpContextAccessor GetHttpContext(string incomingRequestUrl, string host)
{
var context = new DefaultHttpContext();
context.Request.Path = incomingRequestUrl;
context.Request.Host = new HostString(host);
//Do your thing here...
var obj = new HttpContextAccessor();
obj.HttpContext = context;
return obj;
}
这并不直接回答问题的上下文,但它提供了一种替代测试方法,当您开始使用它时,生活会变得更加轻松。
有一个可用于 ASP.NET Core 的集成测试包,可以在此处找到有关它的文档:
https://docs.asp.net/en/latest/testing/integration-testing.html
享受!
您可以创建一个像下面这样继承 HttpContext 的测试类。 并在需要的地方使用测试类。 您可以在代码上添加缺少的实现。
public class TestHttpContext : HttpContext
{
[Obsolete]
public override AuthenticationManager Authentication
{
get { throw new NotImplementedException(); }
}
public override ConnectionInfo Connection
{
get { throw new NotImplementedException(); }
}
public override IFeatureCollection Features
{
get { throw new NotImplementedException(); }
}
public override IDictionary<object, object> Items
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override HttpRequest Request
{
get { throw new NotImplementedException(); }
}
public override CancellationToken RequestAborted
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override IServiceProvider RequestServices
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
HttpResponse _response;
public override HttpResponse Response
{
get
{
if (this._response == null)
{
this._response = new TestHttpResponse();
this._response.StatusCode = 999;
}
return this._response;
}
}
public override ISession Session
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override string TraceIdentifier
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override ClaimsPrincipal User
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override WebSocketManager WebSockets
{
get { throw new NotImplementedException(); }
}
public override void Abort()
{
throw new NotImplementedException();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.