我有一个web服务,我试图单元测试。在服务中,它从HttpContext中提取了几个值,如下所示:

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

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

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

我得到空引用异常,说HttpContext.Current.Session是空的。

是否有方法在单元测试中初始化当前会话?


当前回答

Milox解决方案比IMHO接受的解决方案更好,但我在处理url时遇到了一些问题。

我做了一些改变,使它能正常工作与任何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;
}

其他回答

从不嘲弄。从来没有!解决方法非常简单。为什么要伪造HttpContext这样一个美丽的作品呢?

下推会话!(这句话对我们大多数人来说已经足够理解了,但下面会详细解释)

(字符串)HttpContext.Current.Session(“CustomerId”);是我们现在访问它的方式。将其更改为

_customObject.SessionProperty("CustomerId")

当从test调用时,_customObject使用替代存储(DB或云键值[http://www.kvstore.io/])

但是当从实际应用程序调用时,_customObject使用Session。

这是怎么做到的?嗯…依赖注入!

因此test可以设置会话(地下),然后调用应用程序方法,就好像它对会话一无所知一样。然后测试秘密地检查应用程序代码是否正确地更新了会话。或者应用程序是否基于测试设置的会话值进行操作。

事实上,我们最终还是嘲笑了,尽管我说过:“永远不要嘲笑”。因为我们忍不住溜到下一个规则,“嘲笑最不疼的地方!”模拟巨大的HttpContext或模拟一个小的会话,哪个伤害最小?别问我这些规矩是从哪来的。我们就说常识吧。这里有一篇关于不要嘲笑单元测试会杀死我们的有趣文章

我之前写过一些关于这方面的东西。

单元测试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);
}

我找到了以下在HttpContext中指定用户的简单解决方案:https://forums.asp.net/post/5828182.aspx

对我有效的答案是@Anthony写的,但你必须加上另一行 请求。SetupGet(req => req. headers)。返回(新NameValueCollection ()); 所以你可以用这个: HttpContextFactory.Current.Request.Headers。Add(关键字,值);

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

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

我把这段代码放到一个静态helper类中,如下所示:

public static HttpContext FakeHttpContext()
{
    var httpRequest = new HttpRequest("", "http://example.com/", "");
    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;
}

或者不是使用反射来构造新的httpessionstate实例,你可以将你的httpessionstatecontainer附加到HttpContext(根据Brent M. Spell的评论):

SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);

然后你可以像这样在单元测试中调用它:

HttpContext.Current = MockHelper.FakeHttpContext();