Ustawienie HttpContext.Aktualne.Sesja w teście jednostkowym

Mam serwis internetowy, który próbuję przetestować. W serwisie pobiera kilka wartości z HttpContext w ten sposób:

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

W teście jednostkowym tworzę kontekst używając prostego żądania workera, Tak:

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

Jednakże, gdy próbuję ustawić wartości HttpContext.Current.Session

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

Dostaję null reference exception, który mówi HttpContext.Current.Session jest null.

Czy jest jakiś sposób na zainicjowanie bieżącej sesji w teście jednostkowym?

Author: CodeNotFound, 2012-03-09

13 answers

Musieliśmy wyśmiewać HttpContext używając HttpContextManager i wywołując fabrykę z poziomu naszej aplikacji, a także testów jednostkowych

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

Zamieniasz wszystkie wywołania na HttpContext.Current na HttpContextManager.Current i masz dostęp do tych samych metod. Następnie, gdy testujesz, możesz również uzyskać dostęp do HttpContextManager i wyśmiewać swoje oczekiwania

To jest przykład użycia 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;
}

A następnie użyć go w swoich testach jednostkowych, nazywam to w mojej metodzie Test Init

HttpContextManager.SetCurrentContext(GetMockedHttpContext());

Możesz wtedy, w powyższej metodzie dodaj oczekiwane wyniki z sesji, które spodziewasz się, że będą dostępne w Twoim serwisie internetowym.

 95
Author: Anthony Shaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-27 08:46:10

Możesz" udawać " tworząc nową HttpContext w ten sposób:

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

Wziąłem ten kod i umieściłem go na statycznej klasie pomocniczej w ten sposób:

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

Lub zamiast używać reflection do konstruowania nowej instancji HttpSessionState, możesz po prostu dołączyć swoją HttpSessionStateContainer do HttpContext (zgodnie z komentarzem Brenta M. spella):

SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);

I możesz to nazwać w swoich testach jednostkowych like:

HttpContext.Current = MockHelper.FakeHttpContext();
 261
Author: Milox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-27 08:46:53

Rozwiązanie milox {[3] } jest lepsze od przyjętego IMHO, ale miałem pewne problemy z tą implementacją podczas obsługi adresów URL z querystring .

Wprowadziłem kilka zmian, aby działał poprawnie z dowolnymi adresami URL i aby uniknąć refleksji.

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
Author: giammin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:28

Jakiś czas temu coś o tym pisałem.

Testowanie Jednostkowe HttpContext.Aktualne.Sesja w MVC3. NET

Mam nadzieję, że to pomoże.
[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);
}
 30
Author: Ro Hit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-31 05:48:00

Jeśli używasz frameworka MVC, powinno to zadziałać. Użyłem Miloxa FakeHttpContext i dodałem kilka dodatkowych linii kodu. Pomysł pochodzi z tego postu:

Http://codepaste.net/p269t8

To chyba działa w MVC 5. Nie próbowałem tego we wcześniejszych wersjach 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();
 11
Author: Nimblejoe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:13

Możesz spróbować FakeHttpContext :

using (new FakeHttpContext())
{
   HttpContext.Current.Session["CustomerId"] = "customer1";       
}
 10
Author: vAD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-27 10:41:03

Odpowiedź, która u mnie zadziałała, to to, co napisał @Anthony, ale musisz dodać kolejną linijkę, która jest

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

Więc możesz użyć tego:

HttpContextFactory.Current.Request.Headers.Add(key, value);
 7
Author: yzicus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-24 12:59:49

W asp.net Core / MVC 6 rc2 można ustawić HttpContext

var SomeController controller = new SomeController();

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

Rc 1 było

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

Rozważ użycie Moq

new Mock<ISession>();
 5
Author: KCD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:05

Spróbuj tego:

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

I dodać klasę:

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

To pozwoli Ci przetestować zarówno Sesję, jak i pamięć podręczną.

 1
Author: Isaac Alvarado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-03 20:07:25

Szukałem czegoś mniej inwazyjnego niż opcje wymienione powyżej. W końcu wymyśliłem tandetne rozwiązanie, ale może to sprawić, że niektórzy ludzie będą poruszać się trochę szybciej.

Najpierw stworzyłem TestSession klasę:

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

Następnie dodałem opcjonalny parametr do konstruktora mojego kontrolera. Jeśli parametr jest obecny, użyj go do manipulacji sesją. W przeciwnym razie użyj HttpContext.Sesja:

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

Teraz mogę wstrzyknąć mój TestSession do "kontroler": {]}

class MyControllerTest
{

    private readonly MyController _controller;

    public MyControllerTest()
    {
        var testSession = new TestSession();
        var _controller = new MyController(testSession);
    }
}
 1
Author: Chris Hanson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-24 19:55:30

Nigdy nie kpij.. nigdy! Rozwiązanie jest dość proste. Po co udawać tak piękną kreację jak HttpContext?

Przesuń sesję w dół! (Tylko ta linia wystarczy dla większości z nas, aby zrozumieć, ale wyjaśnione szczegółowo poniżej)

(string)HttpContext.Current.Session["CustomerId"]; tak się teraz do niego dostaniemy. Zmień to na

_customObject.SessionProperty("CustomerId")

Wywołane Z test, _customobject używa alternatywnego store (DB lub Cloud key value [ http://www.kvstore.io/] )

Ale gdy wywołane z prawdziwej aplikacji, _customObject używa Session.

Jak to się robi? cóż... Dependency Injection!

Więc test może ustawić sesję (underground), a następnie wywołać metodę aplikacji tak, jakby nic nie wiedziała o sesji. Następnie test potajemnie sprawdza, czy kod aplikacji poprawnie zaktualizował sesję. Lub jeśli aplikacja zachowuje się w oparciu o wartość sesji ustawioną przez test.

[6]}właściwie, skończyłyśmy na szyderstwie, mimo że powiedziałam: "nigdy nie kpij". Ponieważ nie mogliśmy Nie przejść do następnej zasady, " mock where it boli najmniej!". Naśmiewanie się z ogromnego HttpContext czy naśmiewanie się z małej sesji, co najmniej boli? nie pytaj mnie, skąd wzięły się te zasady. Powiedzmy, że zdrowy rozsądek. Oto interesująca lektura na temat nie wyśmiewania się ponieważ test jednostkowy może nas zabić
 1
Author: Blue Clouds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-11 05:17:20

Odpowiedź @Ro Hit dał mi bardzo pomógł, ale brakowało mi poświadczeń użytkownika, ponieważ musiałem udawać użytkownika do testowania jednostki uwierzytelniania. Opiszę więc, jak to rozwiązałem.

Zgodnie z to , jeśli dodasz metodę

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

A następnie dołącz

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

Do ostatniej linii metody TestSetup, którą wykonałeś, są dodawane poświadczenia użytkownika i gotowe do użycia do testowania uwierzytelniania.

Zauważyłem też, że są Inne części w HttpContext, które możesz potrzebować, takie jak Metoda .MapPath(). Dostępny jest FakeHttpContext, który jest opisany tutaj i może być zainstalowany przez NuGet.

 0
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:00

Znalazłem następujące proste rozwiązanie do określenia użytkownika w HttpContext: https://forums.asp.net/post/5828182.aspx

 0
Author: Lars Ladegaard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-21 13:14:28