2009-02-07 10 views
40

Estoy usando shanselmann's MvcMockHelper clase para burlarme de algunas cosas de HttpContext usando Moq pero el problema que estoy teniendo es poder asignar algo a mi objeto de sesión simulado en mi controlador MVC y luego ser capaz de leer ese mismo valor en mi prueba unitaria para fines de verificación.¿Cómo te burlas de la colección de objetos de sesión usando Moq

Mi pregunta es cómo asignar una colección de almacenamiento al objeto de sesión simulado para permitir código como session ["UserName"] = "foo" para retener el valor "foo" y tenerlo disponible en la unidad de prueba .

Respuesta

58

Comencé con MVCMockHelper de Scott Hanselman, agregué una clase pequeña e hice las modificaciones que se muestran a continuación para permitir que el controlador use normalmente la sesión y la prueba de la unidad para verificar los valores establecidos por el controlador.

/// <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"]); 

No es perfecto, pero funciona para las pruebas.

+1

Gracias por esta muestra, ha sido muy útil. He modificado ligeramente su MockHttpSession para devolver nulo en lugar de arrojar una excepción cuando la clave no existe en el diccionario para imitar más de cerca el objeto HttpSession. Solo un consejo para otros consumidores. – DavidWhitney

+0

Exactamente lo que necesitaba. +1 – fre0n

+0

He intentado hacer esto sin poder actualmente hacer referencia a un marco de burla, y su MockHttpSession es el mejor ejemplo que he encontrado hasta ahora. Descubrí que cambiando el getter como tal obtengo {return _sessionStorage.ContainsKey (name)? _sessionStorage [nombre]: null; } permitirá la prueba de código que se escribe como - if (propiedad de sesión ["alguna clave"] == nulo) {} –

0

Creo que puede establecer una expectativa sobre el simulacro con un valor específico que debe devolver lo que sea. Los simulacros no se usan como falsificaciones reales, sino como cosas sobre las que se puede afirmar el comportamiento.

Parece que en realidad está buscando un adaptador que pueda envolver alrededor de la sesión para que pueda suministrar una implementación diferente durante las pruebas y durante el tiempo de ejecución devolvería los elementos de la sesión HttpContext?

¿Tiene esto sentido?

2

Acabo de encontrar un buen ejemplo de cómo el equipo de Oxite falsifica su HttpSessionState y mantiene una colección SessionStateItemCollection dentro de esa falsificación. Esto debería funcionar tan bien como un moq en mi caso.

EDIT:

URL para este ejemplo es http://oxite.codeplex.com/sourcecontrol/changeset/view/33871?projectName=oxite#388065

+1

para el beneficio de otros que buscan esta pregunta a través de búsquedas, ¿podría publicar un enlace a la información que encontró que responde a la pregunta. –

+0

+1 a un enlace para obtener más información sobre esto. –

+0

Creo que él está hablando de esta clase: http: //oxite.codeplex.com/sourcecontrol/changeset/view/33871? projectName = oxite # 388065 – andrecarlucci

31

Usando Moq 3.0.308.2 aquí es un ejemplo de mi cuenta de configuración del controlador en mi prueba de unidad:

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

entonces dentro de su controlador método lo siguiente debería devolver "Hello World"

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

¡Excelente respuesta! Esto era justo lo que necesitaba para probar los datos de prueba de mi sesión – Rob

+0

Funcionó perfectamente. ¡¡Gracias!! –

3

he hecho un simulacro de un poco más elaborada que la respuesta publicadas por @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

actualizada para reflejar la reparación de la propiedad ** Keys ** - solo necesita otra colección para las claves de seguimiento - por [ esta publicación SO relacionada] (http://stackoverflow.com/a/13277399/175679) – SliverNinja

0

Gracias, @RonnBlack para su solución! En mi caso, seguí recibiendo esta excepción porque Session.SessionID era nulo:

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() 

Para resolver este problema implemento @ código de RonnBlack esta manera el uso de la Moq Mock<HttpSessionStateBase> en lugar de su 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); 
    } 

Para obtener más información, consulte http://weblogs.asp.net/gunnarpeipman/using-moq-to-mock-asp-net-mvc-httpcontextbase

Cuestiones relacionadas