我有一个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是空的。

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


当前回答

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

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

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

_customObject.SessionProperty("CustomerId")

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

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

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

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

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

其他回答

试试这个:

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

这将允许您同时测试会话和缓存。

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

你可以试试FakeHttpContext:

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

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

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