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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9624242/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 08:14:52  来源:igfitidea点击:

Setting HttpContext.Current.Session in a unit test

c#web-servicesunit-testinghttpcontext

提问by DaveB

I have a web service I am trying to unit test. In the service it pulls several values from the HttpContextlike so:

我有一个正在尝试单元测试的 Web 服务。在服务中,它从HttpContext类似的东西中提取了几个值:

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

in the unit test I am creating the context using a simple worker request, like so:

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

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

However, whenever I try to set the values of HttpContext.Current.Session

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

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

I get null reference exception that says HttpContext.Current.Sessionis null.

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

Is there any way to initialize the current session within the unit test?

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

采纳答案by Anthony Shaw

We had to mock HttpContextby using a HttpContextManagerand calling the factory from within our application as well as the Unit Tests

我们必须HttpContext通过使用 aHttpContextManager并从我们的应用程序以及单元测试中调用工厂来进行模拟

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

You would then replace any calls to HttpContext.Currentwith HttpContextManager.Currentand have access to the same methods. Then when you're testing, you can also access the HttpContextManagerand mock your expectations

然后,您可以将任何调用替换为HttpContext.CurrentwithHttpContextManager.Current并访问相同的方法。然后在测试时,您还可以访问HttpContextManager并模拟您的期望

This is an example using Moq:

这是使用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;
}

and then to use it within your unit tests, I call this within my Test Init method

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

HttpContextManager.SetCurrentContext(GetMockedHttpContext());

you can then, in the above method add the expected results from Session that you're expecting to be available to your web service.

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

回答by Milox

You can "fake it" by creating a new HttpContextlike this:

您可以通过创建这样的新来“伪造” HttpContext

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

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

I've taken that code and put it on an static helper class like so:

我已经把那个代码放在一个静态的帮助类上,如下所示:

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

Or instead of using reflection to construct the new HttpSessionStateinstance, you can just attach your HttpSessionStateContainerto the HttpContext(as per Brent M. Spell's comment):

或者不使用反射来构造新HttpSessionState实例,您只需将您HttpSessionStateContainer的附加到HttpContext(根据 Brent M. Spell 的评论):

SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);

and then you can call it in your unit tests like:

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

HttpContext.Current = MockHelper.FakeHttpContext();

回答by yzicus

The answer that worked with me is what @Anthony had written, but you have to add another line which is

与我一起工作的答案是@Anthony 所写的,但您必须添加另一行

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

so you can use this:

所以你可以使用这个:

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

回答by Ro Hit

I worte something about this a while ago.

不久前我写了一些关于这个的东西。

Unit Testing HttpContext.Current.Session in MVC3 .NET

在 MVC3 .NET 中单元测试 HttpContext.Current.Session

Hope it helps.

希望能帮助到你。

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

回答by giammin

Milox solutionis better than the accepted one IMHO but I had some problems with this implementation when handling urls with querystring.

Milox 解决方案比公认的一个更好,恕直言,但我在处理带有 querystring 的 url 时遇到了一些问题

I made some changes to make it work properly with any urls and to avoid Reflection.

我做了一些更改以使其与任何 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;
}

回答by Nimblejoe

If you're using the MVC framework, this should work. I used Milox'sFakeHttpContext and added a few additional lines of code. The idea came from this post:

如果您使用的是 MVC 框架,这应该可以工作。我使用了Milox 的FakeHttpContext 并添加了一些额外的代码行。这个想法来自这篇文章:

http://codepaste.net/p269t8

http://codepaste.net/p269t8

This seems to work in MVC 5. I haven't tried this in earlier versions of MVC.

这似乎在 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();

回答by vAD

You can try FakeHttpContext:

你可以试试FakeHttpContext

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

回答by Isaac Alvarado

Try this:

尝试这个:

        // 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

And Add the class:

并添加类:

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

This will allow you to test with both session and cache.

这将允许您使用会话和缓存进行测试。

回答by KCD

In asp.net Core / MVC 6 rc2 you can set the HttpContext

在 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 was

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

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

Consider using Moq

考虑使用 Moq

new Mock<ISession>();

回答by Matt

The answer @Ro Hitgave helped me a lot, but I was missing the user credentials because I had to fake a user for authentication unit testing. Hence, let me describe how I solved it.

@Ro Hit给出的答案对我帮助很大,但我缺少用户凭据,因为我必须伪造用户进行身份验证单元测试。因此,让我描述一下我是如何解决它的。

According to this, if you add the method

根据这个,如果添加的方法

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

and then append

然后追加

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

to the last line of the TestSetupmethod you're done, the user credentials are added and ready to be used for authentication testing.

TestSetup您完成的方法的最后一行,添加了用户凭据并准备好用于身份验证测试。

I also noticed that there are other parts in HttpContext you might require, such as the .MapPath()method. There is a FakeHttpContext available, which is described hereand can be installed via NuGet.

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