在单元测试中设置HttpContext.Current.Session

Dav*_*veB 180 c# unit-testing web-services httpcontext

我有一个Web服务,我正在尝试进行单元测试.在服务中,它从HttpContext类似物中提取了几个值,因此:

 m_password = (string)HttpContext.Current.Session["CustomerId"];
 m_userID = (string)HttpContext.Current.Session["CustomerUrl"];
Run Code Online (Sandbox Code Playgroud)

在单元测试中,我使用简单的工作者请求创建上下文,如下所示:

SimpleWorkerRequest request = new SimpleWorkerRequest("", "", "", null, new StringWriter());
HttpContext context = new HttpContext(request);
HttpContext.Current = context;
Run Code Online (Sandbox Code Playgroud)

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

HttpContext.Current.Session["CustomerId"] = "customer1";
HttpContext.Current.Session["CustomerUrl"] = "customer1Url";
Run Code Online (Sandbox Code Playgroud)

我得到null引用异常,表示HttpContext.Current.Session为null.

有没有办法在单元测试中初始化当前会话?

Mil*_*lox 287

你可以通过创建一个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;
}
Run Code Online (Sandbox Code Playgroud)

或者不是使用反射来构造新HttpSessionState实例,而是可以将您附加HttpSessionStateContainerHttpContext(根据Brent M. Spell的评论):

SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);
Run Code Online (Sandbox Code Playgroud)

然后你可以在你的单元测试中调用它,如:

HttpContext.Current = MockHelper.FakeHttpContext();
Run Code Online (Sandbox Code Playgroud)

  • 您不必使用反射来构造新的HttpSessionState实例.您可以使用SessionStateUtility.AddHttpSessionStateToContext将HttpSessionStateContainer附加到HttpContext. (28认同)
  • 我比接受的答案更喜欢这个答案,因为更改生产代码以支持您的测试活动是不好的做法.当然,您的生产代码应该抽象出这样的第三方命名空间,但是当您使用遗留代码时,您并不总是有这种控制或奢侈品来重新分配. (24认同)

Ant*_*haw 103

我们不得不HttpContext通过HttpContextManager在我们的应用程序中使用并调用工厂以及单元测试来进行模拟

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您将替换对HttpContext.Currentwith的任何调用,HttpContextManager.Current并且可以访问相同的方法.然后,当您进行测试时,您还可以访问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;
}
Run Code Online (Sandbox Code Playgroud)

然后在你的单元测试中使用它,我在我的Test Init方法中调用它

HttpContextManager.SetCurrentContext(GetMockedHttpContext());
Run Code Online (Sandbox Code Playgroud)

然后,您可以在上面的方法中添加您期望可用于Web服务的Session的预期结果.


gia*_*min 44

Milox解决方案比公认的IMHO更好,但是当处理带有查询字符串的网址时,我遇到了一些问题.

我做了一些更改,使其适用于任何网址,并避免反射.

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;
}
Run Code Online (Sandbox Code Playgroud)


Ro *_*Hit 38

我刚才有点蠢蠢欲动.

单元测试MVC3 .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);
}
Run Code Online (Sandbox Code Playgroud)


Nim*_*joe 12

如果您正在使用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();
Run Code Online (Sandbox Code Playgroud)

  • 链接被破坏所以下次可能把代码放在这里. (2认同)

vAD*_*vAD 11

你可以尝试FakeHttpContext:

using (new FakeHttpContext())
{
   HttpContext.Current.Session["CustomerId"] = "customer1";       
}
Run Code Online (Sandbox Code Playgroud)

  • 效果很好而且使用起来非常简单 (2认同)
  • 遗憾的是与 .NET Core 不兼容 (2认同)

小智 7

与我合作的答案是@Anthony写的,但你必须添加另一行

    request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
Run Code Online (Sandbox Code Playgroud)

所以你可以使用这个:

HttpContextFactory.Current.Request.Headers.Add(key, value);
Run Code Online (Sandbox Code Playgroud)


KCD*_*KCD 7

在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();
Run Code Online (Sandbox Code Playgroud)

rc 1是

var SomeController controller = new SomeController();

controller.ActionContext = new ActionContext();
controller.ActionContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();
Run Code Online (Sandbox Code Playgroud)

/sf/answers/2381607511/

考虑使用 Moq

new Mock<ISession>();
Run Code Online (Sandbox Code Playgroud)