在unit testing中设置HttpContext.Current.Session

我有一个Web服务,我试图unit testing。 在服务中,它从HttpContext中提取几个值,如下所示:

m_password = (string)HttpContext.Current.Session["CustomerId"]; m_userID = (string)HttpContext.Current.Session["CustomerUrl"]; 

在unit testing中,我使用简单的工作请求来创build上下文,如下所示:

 SimpleWorkerRequest request = new SimpleWorkerRequest("", "", "", null, new StringWriter()); HttpContext context = new HttpContext(request); HttpContext.Current = context; 

但是,每当我尝试设置HttpContext.Current.Session的值

 HttpContext.Current.Session["CustomerId"] = "customer1"; HttpContext.Current.Session["CustomerUrl"] = "customer1Url"; 

我得到空引用exception,说HttpContext.Current.Session为空。 有没有什么办法在unit testing中初始化当前会话?

我们必须使用HttpContextManager来模拟HttpContext ,并在应用程序中调用工厂以及unit testing

 public class HttpContextManager { private static HttpContextBase m_context; public static HttpContextBase Current { get { if (m_context != null) return m_context; if (HttpContext.Current == null) throw new InvalidOperationException("HttpContext not available"); return new HttpContextWrapper(HttpContext.Current); } } public static void SetCurrentContext(HttpContextBase context) { m_context = context; } } 

然后,您将使用HttpContextManager.Currentreplace对HttpContext.Current任何调用,并访问相同的方法。 那么当你testing的时候,你也可以访问HttpContextManager并嘲笑你的期望

这是一个使用Moq的例子:

 private HttpContextBase GetMockedHttpContext() { var context = new Mock<HttpContextBase>(); var request = new Mock<HttpRequestBase>(); var response = new Mock<HttpResponseBase>(); var session = new Mock<HttpSessionStateBase>(); var server = new Mock<HttpServerUtilityBase>(); var user = new Mock<IPrincipal>(); var identity = new Mock<IIdentity>(); var urlHelper = new Mock<UrlHelper>(); var routes = new RouteCollection(); MvcApplication.RegisterRoutes(routes); var requestContext = new Mock<RequestContext>(); requestContext.Setup(x => x.HttpContext).Returns(context.Object); context.Setup(ctx => ctx.Request).Returns(request.Object); context.Setup(ctx => ctx.Response).Returns(response.Object); context.Setup(ctx => ctx.Session).Returns(session.Object); context.Setup(ctx => ctx.Server).Returns(server.Object); context.Setup(ctx => ctx.User).Returns(user.Object); user.Setup(ctx => ctx.Identity).Returns(identity.Object); identity.Setup(id => id.IsAuthenticated).Returns(true); identity.Setup(id => id.Name).Returns("test"); request.Setup(req => req.Url).Returns(new Uri("http://www.google.com")); request.Setup(req => req.RequestContext).Returns(requestContext.Object); requestContext.Setup(x => x.RouteData).Returns(new RouteData()); request.SetupGet(req => req.Headers).Returns(new NameValueCollection()); return context.Object; } 

然后在你的unit testing中使用它,我在我的testing初始化​​方法中调用它

 HttpContextManager.SetCurrentContext(GetMockedHttpContext()); 

那么你可以在上面的方法中添加Session的预期结果,你希望可以使用你的Web服务。

你可以通过像这样创build一个新的HttpContext来“伪造”它:

http://www.necronet.org/archive/2010/07/28/unit-testing-code-that-uses-httpcontext-current-session.aspx

我已经把这个代码放在一个静态帮助器类中,像这样:

 public static HttpContext FakeHttpContext() { var httpRequest = new HttpRequest("", "http://stackoverflow/", ""); var stringWriter = new StringWriter(); var httpResponse = new HttpResponse(stringWriter); var httpContext = new HttpContext(httpRequest, httpResponse); var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 10, true, HttpCookieMode.AutoDetect, SessionStateMode.InProc, false); httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, CallingConventions.Standard, new[] { typeof(HttpSessionStateContainer) }, null) .Invoke(new object[] { sessionContainer }); return httpContext; } 

或者不使用reflection来构build新的HttpSessionState实例,只需将HttpSessionStateContainer附加到HttpContext (按照Brent M. Spell的注释):

 SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer); 

然后你可以在你的unit testing中调用它:

 HttpContext.Current = MockHelper.FakeHttpContext(); 

米洛克斯解决scheme比接受的一个恕我直言,但我有处理url与查询string时执行此问题的一些问题 。

我做了一些改变,使其与任何url正常工作,并避免反思。

 public static HttpContext FakeHttpContext(string url) { var uri = new Uri(url); var httpRequest = new HttpRequest(string.Empty, uri.ToString(), uri.Query.TrimStart('?')); var stringWriter = new StringWriter(); var httpResponse = new HttpResponse(stringWriter); var httpContext = new HttpContext(httpRequest, httpResponse); var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 10, true, HttpCookieMode.AutoDetect, SessionStateMode.InProc, false); SessionStateUtility.AddHttpSessionStateToContext( httpContext, sessionContainer); return httpContext; } 

前段时间我对此有所了解。

unit testingMVC3 .NET中的HttpContext.Current.Session

希望它有帮助。

 [TestInitialize] public void TestSetup() { // We need to setup the Current HTTP Context as follows: // Step 1: Setup the HTTP Request var httpRequest = new HttpRequest("", "http://localhost/", ""); // Step 2: Setup the HTTP Response var httpResponce = new HttpResponse(new StringWriter()); // Step 3: Setup the Http Context var httpContext = new HttpContext(httpRequest, httpResponce); var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 10, true, HttpCookieMode.AutoDetect, SessionStateMode.InProc, false); httpContext.Items["AspSession"] = typeof(HttpSessionState) .GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, CallingConventions.Standard, new[] { typeof(HttpSessionStateContainer) }, null) .Invoke(new object[] { sessionContainer }); // Step 4: Assign the Context HttpContext.Current = httpContext; } [TestMethod] public void BasicTest_Push_Item_Into_Session() { // Arrange var itemValue = "RandomItemValue"; var itemKey = "RandomItemKey"; // Act HttpContext.Current.Session.Add(itemKey, itemValue); // Assert Assert.AreEqual(HttpContext.Current.Session[itemKey], itemValue); } 

如果你使用MVC框架,这应该工作。 我使用了Milox的 FakeHttpContext并添加了几行代码。 这个想法来自这个职位:

http://codepaste.net/p269t8

这似乎在MVC 5中工作。我还没有在早期版本的MVC中尝试过。

 HttpContext.Current = MockHttpContext.FakeHttpContext(); var wrapper = new HttpContextWrapper(HttpContext.Current); MyController controller = new MyController(); controller.ControllerContext = new ControllerContext(wrapper, new RouteData(), controller); string result = controller.MyMethod(); 

你可以试试FakeHttpContext :

 using (new FakeHttpContext()) { HttpContext.Current.Session["CustomerId"] = "customer1"; } 

和我一起工作的答案是@Anthony写的,但是你必须添加另外一行

  request.SetupGet(req => req.Headers).Returns(new NameValueCollection()); 

所以你可以使用这个:

 HttpContextFactory.Current.Request.Headers.Add(key, value); 

在asp.net Core / MVC 6 RC2中,你可以设置HttpContext

 var SomeController controller = new SomeController(); controller.ControllerContext = new ControllerContext(); controller.ControllerContext.HttpContext = new DefaultHttpContext(); controller.HttpContext.Session = new DummySession(); 

rc 1是

 var SomeController controller = new SomeController(); controller.ActionContext = new ActionContext(); controller.ActionContext.HttpContext = new DefaultHttpContext(); controller.HttpContext.Session = new DummySession(); 

https://stackoverflow.com/a/34022964/516748

考虑使用Moq

 new Mock<ISession>(); 

尝试这个:

  // MockHttpSession Setup var session = new MockHttpSession(); // MockHttpRequest Setup - mock AJAX request var httpRequest = new Mock<HttpRequestBase>(); // Setup this part of the HTTP request for AJAX calls httpRequest.Setup(req => req["X-Requested-With"]).Returns("XMLHttpRequest"); // MockHttpContextBase Setup - mock request, cache, and session var httpContext = new Mock<HttpContextBase>(); httpContext.Setup(ctx => ctx.Request).Returns(httpRequest.Object); httpContext.Setup(ctx => ctx.Cache).Returns(HttpRuntime.Cache); httpContext.Setup(ctx => ctx.Session).Returns(session); // MockHttpContext for cache var contextRequest = new HttpRequest("", "http://localhost/", ""); var contextResponse = new HttpResponse(new StringWriter()); HttpContext.Current = new HttpContext(contextRequest, contextResponse); // MockControllerContext Setup var context = new Mock<ControllerContext>(); context.Setup(ctx => ctx.HttpContext).Returns(httpContext.Object); //TODO: Create new controller here // Set controller's ControllerContext to context.Object 

并添加类:

 public class MockHttpSession : HttpSessionStateBase { Dictionary<string, object> _sessionDictionary = new Dictionary<string, object>(); public override object this[string name] { get { return _sessionDictionary.ContainsKey(name) ? _sessionDictionary[name] : null; } set { _sessionDictionary[name] = value; } } public override void Abandon() { var keys = new List<string>(); foreach (var kvp in _sessionDictionary) { keys.Add(kvp.Key); } foreach (var key in keys) { _sessionDictionary.Remove(key); } } public override void Clear() { var keys = new List<string>(); foreach (var kvp in _sessionDictionary) { keys.Add(kvp.Key); } foreach(var key in keys) { _sessionDictionary.Remove(key); } } } 

这将允许您testing会话和caching。

我正在寻找一些比上面提到的选项更less侵入的东西。 最后,我提出了一个俗气的解决scheme,但可能会让一些人快一点。

首先我创build了一个TestSession类:

 class TestSession : ISession { public TestSession() { Values = new Dictionary<string, byte[]>(); } public string Id { get { return "session_id"; } } public bool IsAvailable { get { return true; } } public IEnumerable<string> Keys { get { return Values.Keys; } } public Dictionary<string, byte[]> Values { get; set; } public void Clear() { Values.Clear(); } public Task CommitAsync() { throw new NotImplementedException(); } public Task LoadAsync() { throw new NotImplementedException(); } public void Remove(string key) { Values.Remove(key); } public void Set(string key, byte[] value) { if (Values.ContainsKey(key)) { Remove(key); } Values.Add(key, value); } public bool TryGetValue(string key, out byte[] value) { if (Values.ContainsKey(key)) { value = Values[key]; return true; } value = new byte[0]; return false; } } 

然后我添加一个可选参数到我的控制器的构造函数。 如果该参数存在,则将其用于会话操作。 否则,使用HttpContext.Session:

 class MyController { private readonly ISession _session; public MyController(ISession session = null) { _session = session; } public IActionResult Action1() { Session().SetString("Key", "Value"); View(); } public IActionResult Action2() { ViewBag.Key = Session().GetString("Key"); View(); } private ISession Session() { return _session ?? HttpContext.Session; } } 

现在我可以将我的TestSession注入到控制器中:

 class MyControllerTest { private readonly MyController _controller; public MyControllerTest() { var testSession = new TestSession(); var _controller = new MyController(testSession); } } 

答案@Ro Hit给了我很多帮助,但我错过了用户凭证,因为我不得不伪造用户进行authenticationunit testing。 因此,让我来描述我是如何解决这个问题的。

据此,如果添加该方法

  // using System.Security.Principal; GenericPrincipal FakeUser(string userName) { var fakeIdentity = new GenericIdentity(userName); var principal = new GenericPrincipal(fakeIdentity, null); return principal; } 

然后追加

  HttpContext.Current.User = FakeUser("myDomain\\myUser"); 

TestSetup方法的最后一行,用户凭证将被添加并准备用于身份validationtesting。

我还注意到,你可能需要HttpContext中的其他部分,比如.MapPath()方法。 有一个FakeHttpContext可用, 这里描述 ,可以通过NuGet安装。