2009-02-07 6 views
40

J'utilise la classe shanselmann's MvcMockHelper pour maqueter des choses HttpContext en utilisant Moq mais le problème que je rencontre est de pouvoir assigner quelque chose à mon objet de session mocké dans mon contrôleur MVC et d'être ensuite capable de lire cette même valeur dans mon test unitaire à des fins de vérification. Ma question est de savoir comment affecter une collection de stockage à l'objet de session mocked pour permettre au code tel que session ["UserName"] = "foo" de conserver la valeur "foo" et doit être disponible dans le test unitaire .Comment moquerez-vous la collection d'objets de session en utilisant Moq

Répondre

58

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

/// <summary> 
/// A Class to allow simulation of SessionObject 
/// </summary> 
public class MockHttpSession : HttpSessionStateBase 
{ 
    Dictionary<string, object> m_SessionStorage = new Dictionary<string, object>(); 

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

//In the MVCMockHelpers I modified the FakeHttpContext() method as shown below 
public static HttpContextBase FakeHttpContext() 
{ 
    var context = new Mock<HttpContextBase>(); 
    var request = new Mock<HttpRequestBase>(); 
    var response = new Mock<HttpResponseBase>(); 
    var session = new MockHttpSession(); 
    var server = new Mock<HttpServerUtilityBase>(); 

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

//Now in the unit test i can do 
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 cela fonctionne assez pour les tests.

+1

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

+0

Exactement ce dont j'avais besoin. +1 – fre0n

+0

J'ai essayé de le faire sans pouvoir actuellement référencer un cadre de simulation, et votre MockHttpSession est le meilleur exemple que j'ai trouvé jusqu'ici. J'ai découvert que changer le getter en tant que tel obtenait {return _sessionStorage.ContainsKey (name)? _sessionStorage [nom]: null; } permettra le test du code qui est écrit comme - if (sessionProperty ["une certaine clé"] == null) {} –

0

Je pense que vous pouvez définir une attente sur le faux avec une valeur spécifique, il devrait retourner quoi que ce soit. Les mocks ne sont pas utilisés comme des faux réels mais plutôt comme des choses sur lesquelles vous pouvez affirmer un comportement.

Il semble que vous cherchiez en fait un adaptateur que vous pouvez enrouler autour de la session pour pouvoir fournir une implémentation différente pendant les tests et pendant l'exécution il retournera des éléments de session HttpContext?

Est-ce que cela a du sens?

2

Je viens de trouver un bel exemple de la fausseté de leur HttpSessionState par l'équipe Oxite et de la maintenance d'une collection SessionStateItemCollection dans ce faux. 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

pour le bénéfice de d'autres trouvant cette question à travers des recherches, pourriez-vous s'il vous plaît poster un lien vers l'information que vous avez trouvé qui répond à la question. –

+0

+1 à un lien pour plus d'informations à ce sujet. –

+0

Je pense qu'il parle de cette classe: http: //oxite.codeplex.com/sourcecontrol/changeset/view/33871? projectName = oxite # 388065 – andrecarlucci

31

L'utilisation moq 3.0.308.2 est un exemple ici de mon compte la configuration du contrôleur dans mon test unitaire:

private AccountController GetAccountController() 
    { 
     .. setup mocked services.. 

     var accountController = new AccountController (..mocked services..); 

     var controllerContext = new Mock<ControllerContext>(); 
     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; 
    } 

puis au sein de votre contrôleur méthode ce qui suit devrait renvoyer "Hello World"

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

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

+0

Fonctionne parfaitement. Je vous remercie!! –

3

Je l'ai fait une maquette un peu plus élaborée que la réponse Posté par @RonnBlack

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

    private readonly Dictionary<string, object> _values = new Dictionary<string, object>(); 

    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<string, object> 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

mis à jour pour refléter la correction de la propriété ** Keys ** - juste besoin d'une autre collection pour les clés de suivi - per [ ce poste connexe SO] (http://stackoverflow.com/a/13277399/175679) – SliverNinja

0

Merci, @RonnBlack pour votre solution! Dans mon cas, je continué à obtenir cette exception parce Session.SessionID était nulle:

System.NotImplementedException was unhandled by user code 
    HResult=-2147467263 
    Message=The method or operation is not implemented. 
    Source=System.Web 
    StackTrace: 
     at System.Web.HttpSessionStateBase.get_SessionID() 

Pour résoudre ce problème je mets en œuvre @ code de RonnBlack cette façon en utilisant le Moq Mock<HttpSessionStateBase> au lieu de son MockHttpSession:

private readonly MyController controller = new MyController(); 

    [TestFixtureSetUp] 
    public void Init() 
    { 
     var session = new Mock<HttpSessionStateBase>(); 
     session.Setup(s => s.SessionID).Returns(Guid.NewGuid().ToString()); 
     var request = new Mock<HttpRequestBase>(); 
     var response = new Mock<HttpResponseBase>(); 
     var server = new Mock<HttpServerUtilityBase>(); 
     // Not working - IsAjaxRequest() is static extension method and cannot be mocked 
     // request.Setup(x => x.IsAjaxRequest()).Returns(true /* or false */); 
     // use this 
     request.SetupGet(x => x.Headers).Returns(
      new System.Net.WebHeaderCollection 
      { 
       {"X-Requested-With", "XMLHttpRequest"} 
      }); 

     var context = new Mock<HttpContextBase>(); 
     //context 
     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.SetupGet(x => x.Request).Returns(request.Object); 
     context.SetupGet(p => p.Request.Url).Returns(new Uri("http://www.mytesturl.com")); 
     var queryString = new NameValueCollection { { "code", "codeValue" } }; 
     context.SetupGet(r => r.Request.QueryString).Returns(queryString); 

     controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller); 
    } 

Pour plus de détails, s'il vous plaît voir http://weblogs.asp.net/gunnarpeipman/using-moq-to-mock-asp-net-mvc-httpcontextbase

Questions connexes