Paramétrage De HttpContext.Actuel.Session dans un test unitaire

j'ai un service web que j'essaie de tester en unité. Dans le service Il tire plusieurs valeurs du HttpContext comme ainsi:

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

dans le test de l'Unité, je crée le contexte en utilisant une simple demande de travail, comme ceci:

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

Cependant, chaque fois que j'essaie de définir les valeurs de HttpContext.Current.Session

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

je reçois référence nulle exception qui dit HttpContext.Current.Session est null.

est il un moyen d'initialiser la session en cours au sein de l'unité de test?

160
demandé sur CodeNotFound 2012-03-09 00:15:31

13 réponses

nous avons dû simuler HttpContext en utilisant un HttpContextManager et en appelant l'usine de notre application ainsi que les tests de L'Unité

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

vous remplacerez alors tous les appels à HttpContext.Current par HttpContextManager.Current et aurez accès aux mêmes méthodes. Ensuite, lorsque vous faites des tests, vous pouvez également accéder au HttpContextManager et vous moquer de vos attentes

c'est un exemple qui utilise 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;
}

et ensuite pour l'utiliser dans vos tests unitaires, je l'appelle dans ma méthode D'essai Init

HttpContextManager.SetCurrentContext(GetMockedHttpContext());

vous pouvez alors, dans la méthode ci-dessus ajouter les résultats attendus de la Session que vous attendez d'être disponible pour votre service web.

95
répondu Anthony Shaw 2018-04-27 08:46:10

vous pouvez "faire semblant" en créant un nouveau HttpContext comme ceci:

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

j'ai pris ce code et l'ai mis sur une classe d'Assistant statique comme ça:

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

ou au lieu d'utiliser la réflexion pour construire la nouvelle instance HttpSessionState , vous pouvez simplement attacher votre HttpSessionStateContainer HttpContext (selon le commentaire de Brent M. Spell):

SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);

et ensuite vous pouvez l'appeler dans vos tests unitaires comme:

HttpContext.Current = MockHelper.FakeHttpContext();
261
répondu Milox 2018-04-27 08:46:53

Milox solution est meilleur que celui accepté IMHO mais j'ai eu quelques problèmes avec cette implémentation lors de la manipulation des urls avec querystring .

j'ai fait quelques changements pour le faire fonctionner correctement avec n'importe quelle URL et pour éviter la réflexion.

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
répondu giammin 2017-05-23 10:31:28

j'en ai parlé il y a un moment.

"151940920 De Tests Unitaires, HttpContext.Actuel.Session dans MVC3. net

J'espère que ça aidera.

[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
répondu Ro Hit 2013-10-31 05:48:00

si vous utilisez le framework MVC, cela devrait fonctionner. J'ai utilisé Milox FakeHttpContext et ajouté quelques lignes de code supplémentaires. L'idée est venue de ce post:

http://codepaste.net/p269t8

cela semble fonctionner dans MVC 5. Je n'ai pas essayé cela dans les versions précédentes de 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
répondu Nimblejoe 2017-05-23 11:47:13

vous pouvez essayer FakeHttpContext :

using (new FakeHttpContext())
{
   HttpContext.Current.Session["CustomerId"] = "customer1";       
}
10
répondu vAD 2015-07-27 10:41:03

la réponse qui a fonctionné avec moi est ce que @Anthony avait écrit, mais vous devez ajouter une autre ligne qui est

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

vous pouvez donc utiliser ceci:

HttpContextFactory.Current.Request.Headers.Add(key, value);
7
répondu yzicus 2013-09-24 12:59:49

In asp.net Core / MVC 6 rc2 vous pouvez définir le HttpContext

var SomeController controller = new SomeController();

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

rc 1 a été

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

envisager d'utiliser Moq

new Mock<ISession>();
5
répondu KCD 2017-05-23 12:10:05

essayez ceci:

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

et ajouter la classe:

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

Cela vous permettra de tester à la fois la session et le cache.

1
répondu Isaac Alvarado 2016-02-03 20:07:25

je cherchais quelque chose d'un peu moins invasive que les options mentionnées ci-dessus. À la fin, j'ai trouvé une solution ringarde, mais ça pourrait faire bouger les gens un peu plus vite.

j'ai d'Abord créé un TestSession classe:

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

puis j'ai ajouté un paramètre optionnel au constructeur de mon contrôleur. Si le paramètre est présent, utilisez-le pour la manipulation de session. Sinon, utilisez le 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;
    }
}

maintenant je peux injecter mon TestSession dans le contrôleur:

class MyControllerTest
{

    private readonly MyController _controller;

    public MyControllerTest()
    {
        var testSession = new TestSession();
        var _controller = new MyController(testSession);
    }
}
1
répondu Chris Hanson 2017-01-24 19:55:30

ne vous moquez jamais.. jamais!!!! La solution est assez simple. Pourquoi simuler une si belle création comme HttpContext ?

repoussez la session! (Juste cette ligne est assez pour la plupart d'entre nous comprendre mais expliqué en détail ci-dessous)

(string)HttpContext.Current.Session["CustomerId"]; est la façon dont nous y accédons maintenant. Au lieu de

_customObject.SessionProperty("CustomerId")

lorsqu'il est appelé à partir de test, _customObject utilise la valeur de stockage alternative (DB ou clé de nuage [ http://www.kvstore.io/] )

mais quand appelé de l'application réelle, _customObject utilise Session .

comment faire? bien... Injection De Dépendance!

ainsi test peut définir la session(underground) et ensuite appeler la méthode d'application comme si elle ne sait rien sur la session. Puis testez secrètement vérifie si le code d'application correctement mis à jour la session. Ou si l'application se comporte en fonction de la session valeur fixée par le test.

en fait, nous avons fini par nous moquer même si j'ai dit: "Ne te moque jamais". Parce qu'on n'a pas pu s'empêcher de glisser vers la règle suivante: "moquez-vous là où ça fait le moins mal!". Se moquer de l'énorme HttpContext ou se moquer d'une session minuscule, ce qui fait le moins mal? ne me demande pas d'où viennent ces règles. Disons simplement le bon sens. Voici une lecture intéressante sur ne pas se moquer comme le test de l'unité peut nous tuer

1
répondu Blue Clouds 2018-08-11 05:17:20

la réponse @Ro Hit m'a beaucoup aidé, mais je manquais les informations d'identification de l'utilisateur parce que je devais simuler un utilisateur pour le test de l'unité d'authentification. Permettez-moi donc de décrire comment je l'ai résolu.

Selon ce , si vous ajoutez la méthode

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

et ajouter

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

à la dernière ligne de la méthode TestSetup vous êtes fait, les justificatifs d'identité de l'utilisateur sont ajoutés et prêts à être utilisés pour le test d'authentification.

j'ai aussi remarqué qu'il y a d'autres parties dans HttpContext dont vous pourriez avoir besoin, comme la méthode .MapPath() . Il existe un context Fakehttp disponible, qui est décrit ici et peut être installé via NuGet.

0
répondu Matt 2017-05-23 12:18:00

j'ai trouvé la solution simple suivante pour spécifier un utilisateur dans le HttpContext: https://forums.asp.net/post/5828182.aspx

0
répondu Lars Ladegaard 2018-02-21 13:14:28