it-swarm.com.de

Einstellung von HttpContext.Current.Session in einem Komponententest

Ich habe einen Webservice, den ich zum Komponententest versuche. Im Dienst werden einige Werte aus der HttpContext abgerufen:

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

im Unit-Test erstelle ich den Kontext mit einer einfachen Worker-Anfrage:

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

Immer wenn ich versuche, die Werte von HttpContext.Current.Session einzustellen

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

Ich erhalte eine Nullreferenzausnahme, die besagt, dass HttpContext.Current.Session null ist. 

Gibt es eine Möglichkeit, die aktuelle Sitzung innerhalb des Komponententests zu initialisieren?

164
DaveB

Wir mussten HttpContext überlegen, indem wir eine HttpContextManager verwenden und die Factory innerhalb unserer Anwendung sowie der Unit-Tests aufrufen

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

Sie würden dann alle Aufrufe von HttpContext.Current durch HttpContextManager.Current ersetzen und auf dieselben Methoden zugreifen. Wenn Sie dann testen, können Sie auch auf die Variable HttpContextManager zugreifen und Ihre Erwartungen verhöhnen

Dies ist ein Beispiel mit 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;
}

und um es dann in Ihren Unit-Tests zu verwenden, rufe ich dies in meiner Test Init-Methode auf

HttpContextManager.SetCurrentContext(GetMockedHttpContext());

sie können dann in der obigen Methode die erwarteten Ergebnisse aus der Sitzung hinzufügen, von denen Sie erwarten, dass sie für Ihren Webdienst verfügbar sind.

95
Anthony Shaw

Sie können es "fälschen", indem Sie eine neue HttpContext wie folgt erstellen:

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

Ich habe diesen Code in eine statische Hilfsklasse aufgenommen: 

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

Anstatt die Reflektion zu verwenden, um die neue HttpSessionState-Instanz zu erstellen, können Sie Ihre HttpSessionStateContainer einfach an die HttpContext anhängen (wie im Kommentar von Brent M. Spell):

SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);

und dann können Sie es in Ihren Unit-Tests aufrufen wie:

HttpContext.Current = MockHelper.FakeHttpContext();
268
Milox

Milox-Lösung ist besser als die akzeptierte IMHO, aber Ich hatte einige Probleme mit dieser Implementierung beim Umgang mit URLs mit Querystring .

Ich habe einige Änderungen vorgenommen, damit alle URLs ordnungsgemäß funktionieren und Spiegelungen vermieden werden.

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

Ich habe vor einiger Zeit etwas darüber gesagt. 

Einheitstest HttpContext.Current.Session in MVC3 .NET

Ich hoffe es hilft.

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

Wenn Sie das MVC-Framework verwenden, sollte dies funktionieren. Ich habe Milox's FakeHttpContext verwendet und ein paar zusätzliche Codezeilen hinzugefügt. Die Idee kam aus diesem Beitrag:

http://codepaste.net/p269t8

Dies scheint in MVC 5 zu funktionieren. Ich habe dies in früheren MVC-Versionen noch nicht versucht.

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();
12
Nimblejoe

Sie können versuchen, FakeHttpContext :

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

Die Antwort, die bei mir funktioniert hat, ist, was @Anthony geschrieben hat, aber Sie müssen eine weitere Zeile hinzufügen, die ist 

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

so können Sie dies verwenden:

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

In asp.net Core/MVC 6 rc2 können Sie die HttpContext einstellen.

var SomeController controller = new SomeController();

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

rc 1 war

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

Erwägen Sie die Verwendung von Moq

new Mock<ISession>();
5
KCD

Nie verspotten ... niemals! Die Lösung ist ziemlich einfach. Warum eine so schöne Kreation wie HttpContext fälschen? 

Drücken Sie die Sitzung nach unten! (Diese Zeile reicht für die meisten von uns aus, wird aber im Folgenden näher erläutert.)

(string)HttpContext.Current.Session["CustomerId"]; ist, wie wir jetzt darauf zugreifen. Ändern Sie dies in 

_customObject.SessionProperty("CustomerId")

Beim Aufruf aus dem Test verwendet _customObject einen alternativen Speicher (DB- oder Cloud-Schlüsselwert [ http://www.kvstore.io/] ).

Beim Aufruf aus der realen Anwendung verwendet _customObjectSession.

wie wird das gemacht? gut ... Abhängigkeitsinjektion! 

Test kann also die Session einstellen (Underground) und dann die Applikationsmethode aufrufen, als ob sie nichts über die Session weiß. Dann testet heimlich, ob der Anwendungscode die Sitzung korrekt aktualisiert hat. Oder wenn sich die Anwendung auf der Grundlage des Sitzungswerts verhält, der vom Test festgelegt wurde.

Eigentlich haben wir verspottet, obwohl ich sagte: "Nie verspotten". Weil wir nicht anders konnten, als zur nächsten Regel zu rutschen: "Spott, wo es am wenigsten weh tut!". Verspottung großer HttpContext oder Verspottung einer winzigen Sitzung, die am wenigsten weh tut? Frag mich nicht, woher diese Regeln kommen. Sagen wir einfach den gesunden Menschenverstand. Hier ist eine interessante Lektüre über nicht spottend da Unit-Test uns töten kann

1
Blue Clouds

Versuche dies: 

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

Und füge die Klasse hinzu:

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

Dadurch können Sie mit Sitzung und Cache testen.

1
Isaac Alvarado

Ich suchte etwas weniger invasives als die oben genannten Optionen. Am Ende habe ich eine kitschige Lösung gefunden, aber es könnte ein paar Leute ein bisschen schneller gehen.

Zuerst habe ich eine TestSession Klasse erstellt:

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

Dann fügte ich dem Konstruktor meines Controllers einen optionalen Parameter hinzu. Wenn der Parameter vorhanden ist, verwenden Sie ihn zur Sitzungsmanipulation. Verwenden Sie andernfalls die 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;
    }
}

Jetzt kann ich meine TestSession in die Steuerung einspritzen:

class MyControllerTest
{

    private readonly MyController _controller;

    public MyControllerTest()
    {
        var testSession = new TestSession();
        var _controller = new MyController(testSession);
    }
}
1
Chris Hanson

Die Antwort @Ro Hit hat mir sehr geholfen, aber mir fehlten die Anmeldeinformationen des Benutzers, da ich einen Benutzer für Authentifizierungstests vormachen musste. Lassen Sie mich daher beschreiben, wie ich es gelöst habe.

Nach this , wenn Sie die Methode hinzufügen

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

und dann anhängen

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

in der letzten Zeile der TestSetup-Methode sind Sie fertig, die Benutzeranmeldeinformationen werden hinzugefügt und können für Authentifizierungstests verwendet werden.

Ich habe auch bemerkt, dass es andere Teile in HttpContext gibt, die Sie möglicherweise benötigen, z. B. die .MapPath()-Methode. Es steht ein FakeHttpContext zur Verfügung, der hier beschrieben ist und über NuGet installiert werden kann.

0
Matt

Versuchen Sie es auf diese Weise ..

public static HttpContext getCurrentSession()
  {
        HttpContext.Current = new HttpContext(new HttpRequest("", ConfigurationManager.AppSettings["UnitTestSessionURL"], ""), new HttpResponse(new System.IO.StringWriter()));
        System.Web.SessionState.SessionStateUtility.AddHttpSessionStateToContext(
        HttpContext.Current, new HttpSessionStateContainer("", new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 20000, true,
        HttpCookieMode.UseCookies, SessionStateMode.InProc, false));
        return HttpContext.Current;
  }
0
Ranjan Singh

Ich habe im HttpContext die folgende einfache Lösung gefunden, um einen Benutzer anzugeben: https://forums.asp.net/post/5828182.aspx

0
Lars Ladegaard