45 votes

Comment simuler la collection d'objets de session en utilisant Moq

Je utilise la classe MvcMockHelper de shanselmann pour simuler certaines choses HttpContext en utilisant Moq mais le problème que j'ai est de pouvoir assigner quelque chose à mon objet session simulé dans mon contrôleur MVC et ensuite pouvoir lire cette même valeur dans mon test unitaire à des fins de vérification.

Ma question est comment assigner une collection de stockage à l'objet session simulé pour permettre du code tel que session["UserName"] = "foo" de conserver la valeur "foo" et de la rendre disponible dans le test unitaire.

64voto

RonnBlack Points 1199

J'ai commencé avec le MVCMockHelper de Scott Hanselman, ajouté une petite classe et apporté les modifications ci-dessous pour permettre au contrôleur d'utiliser la Session normalement et au test unitaire de vérifier les valeurs qui ont été définies par le contrôleur.

/// 
/// Une classe permettant de simuler un objet Session
/// 
public class MockHttpSession : HttpSessionStateBase
{
    Dictionary m_SessionStorage = new Dictionary();

    public override object this[string name]
    {
        get { return m_SessionStorage[name]; }
        set { m_SessionStorage[name] = value; }
    }
}

//Dans MVCMockHelpers, j'ai modifié la méthode FakeHttpContext() comme indiqué ci-dessous
public static HttpContextBase FakeHttpContext()
{
    var context = new Mock();
    var request = new Mock();
    var response = new Mock();
    var session = new MockHttpSession();
    var server = new Mock();

    context.Setup(ctx => ctx.Request).Returns(request.Object);
    context.Setup(ctx => ctx.Response).Returns(response.Object);
    context.Setup(ctx => ctx.Session).Returns(session);
    context.Setup(ctx => ctx.Server).Returns(server.Object);

    return context.Object;
}

//Maintenant dans le test unitaire, je peux faire
AccountController acct = new AccountController();
acct.SetFakeControllerContext();
acct.SetBusinessObject(mockBO.Object);

RedirectResult results = (RedirectResult)acct.LogOn(userName, password, rememberMe, returnUrl);
Assert.AreEqual(returnUrl, results.Url);
Assert.AreEqual(userName, acct.Session["txtUserName"]);
Assert.IsNotNull(acct.Session["SessionGUID"]);

Ce n'est pas parfait mais ça suffit pour les tests.

2 votes

Merci pour cet exemple, il a été très utile. J'ai légèrement modifié votre MockHttpSession pour renvoyer null plutôt que de générer une exception lorsque la clé n'existe pas dans le dictionnaire, afin de plus étroitement imiter l'objet HttpSession. Juste un conseil pour les autres consommateurs.

0 votes

J'ai essayé de faire cela sans pouvoir actuellement faire référence à un framework de moquerie, et votre MockHttpSession est le meilleur exemple que j'ai trouvé jusqu'à présent. J'ai découvert qu'en changeant le getter comme tel get { return _sessionStorage.ContainsKey(name) ? _sessionStorage[name] : null; }, cela permettra de tester du code qui est écrit comme -- if (sessionProperty["some key"] == null){}

38voto

Todd Smith Points 8297

En utilisant Moq 3.0.308.2, voici un exemple de configuration de mon contrôleur de compte dans mon test unitaire :

    private AccountController GetAccountController ()
    {
      .. configuration des services simulés ..

      var accountController = new AccountController (..services simulés..);

      var controllerContext = new Mock ();
      controllerContext.SetupGet(p => p.HttpContext.Session["test"]).Returns("Hello World");
      controllerContext.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(_testEmail);
      controllerContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
      controllerContext.SetupGet(p => p.HttpContext.Response.Cookies).Returns(new HttpCookieCollection ());

      controllerContext.Setup (p => p.HttpContext.Request.Form.Get ("ReturnUrl")).Returns ("sample-return-url");
      controllerContext.Setup (p => p.HttpContext.Request.Params.Get ("q")).Returns ("sample-search-term");

      accountController.ControllerContext = controllerContext.Object;

      return accountController;
    }

ensuite, dans votre méthode de contrôleur, le code suivant devrait retourner "Hello World"

string test = Session["test"].ToString ();

0 votes

Super réponse! C'était exactement ce dont j'avais besoin pour tester les données de ma session.

0 votes

A bien fonctionné. Merci!!

3voto

Chris Marisic Points 11495

J'ai créé un Mock légèrement plus élaboré que la réponse publiée par @RonnBlack

public class HttpSessionStateDictionary : HttpSessionStateBase
{
    private readonly NameValueCollection keyCollection = new NameValueCollection();

    private readonly Dictionary _values = new Dictionary();

    public override object this[string name]
    {
        get { return _values.ContainsKey(name) ? _values[name] : null; }
        set { _values[name] = value; keyCollection[name] = null;}
    }

    public override int CodePage
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    public override HttpSessionStateBase Contents
    {
        get { throw new NotImplementedException(); }
    }

    public override HttpCookieMode CookieMode
    {
        get { throw new NotImplementedException(); }
    }

    public override int Count
    {
        get { return _values.Count; }
    }

     public override NameObjectCollectionBase.KeysCollection Keys
{
    get { return keyCollection.Keys; }
}

    public Dictionary UnderlyingStore
    {
        get { return _values; }
    }

    public override void Abandon()
    {
        _values.Clear();
    }

    public override void Add(string name, object value)
    {
        _values.Add(name, value);
    }

    public override void Clear()
    {
        _values.Clear();
    }

    public override void CopyTo(Array array, int index)
    {
        throw new NotImplementedException();
    }

    public override bool Equals(object obj)
    {
        return _values.Equals(obj);
    }

    public override IEnumerator GetEnumerator()
    {
        return _values.GetEnumerator();
    }

    public override int GetHashCode()
    {
        return (_values != null ? _values.GetHashCode() : 0);
    }

    public override void Remove(string name)
    {
        _values.Remove(name);
    }

    public override void RemoveAll()
    {
        _values.Clear();
    }

    public override void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }

    public override string ToString()
    {
        return _values.ToString();
    }

    public bool Equals(HttpSessionStateDictionary other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other._values, _values);
    }
}

0 votes

Mis à jour pour refléter la correction de la propriété Keys - il suffit juste d'une autre collection pour suivre les clés - selon ce post SO lié

2voto

rayray2030 Points 190

Je viens de trouver un bel exemple de la façon dont l'équipe Oxite simule leur HttpSessionState et maintient une collection SessionStateItemCollection à l'intérieur de cette simulation. Cela devrait fonctionner aussi bien qu'un moq dans mon cas.

EDIT:

URL pour cet exemple est http://oxite.codeplex.com/sourcecontrol/changeset/view/33871?projectName=oxite#388065

1 votes

Dans le but d'aider les autres à trouver cette question via des recherches, pourriez-vous s'il vous plaît poster un lien vers les informations que vous avez trouvées qui répondent à la question.

0 votes

Je pense qu'il parle de cette classe : oxite.codeplex.com/sourcecontrol/changeset/view/…

0voto

Sean Chambers Points 3159

Je pense que vous pouvez définir une attente sur le mock avec une valeur spécifique qu'il devrait renvoyer quoi qu'il arrive. Les mocks ne sont pas utilisés comme de vrais faux, mais plutôt comme des éléments sur lesquels vous pouvez affirmer un comportement.

Il semble que vous recherchiez en fait un adaptateur que vous pouvez envelopper autour de la session et pour lequel vous pouvez fournir une implémentation différente pendant les tests et, pendant l'exécution, il renverrait les éléments de la session HttpContext ?

Cela a du sens ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X