December 2011 - Artículos

Muy buenas!

Coged a alguien que no conozca mucho ASP.NET y preguntadle que relación tienen las siguientes clases entre ellas:

  1. HttpRequest
  2. HttpRequestBase
  3. HttpRequestWrapper

La respuesta más probable será que HttpRequestBase es la clase base, de la cual deriva HttpRequest y que HttpRequestWrapper es… bueno, por el nombre no queda muy claro: es un wrapper de algo pero de qué?

Pues no. Nada más lejos de la realidad. Aunque el nombre sugiera lo contrario HttpRequestBase no es la clase base de HttpRequest, de hecho ambas clases no tienen relación alguna entre ellas, pero en Redmond no tuvieron un buen día al escoger el nombre… Aunque al menos debe reconocerse que HttpRequestBase sí es clase base de alguien… ¡concretamente de HttpRequestWrapper!

Dado que no hay mucha gente que conozca esas dos últimas clases, hagamos pues una pequeña presentación.

HttpRequestBase es una clase cuyo interfaz público (es decir sus métodos y propiedades) son idénticos a los de HttpRequest. ¿Que HttpRequest tiene una propiedad llamada Cookies? HttpRequestBase la tiene también… y del mismo tipo. Y así con todas las propiedades y todos los métodos.

¿Y por qué han decidido crear semejante… cosa?

Pues para corregir una carencia que tenía el Framework: permitir abstraernos de HttpRequest de forma fácil: HttpRequestBase tiene la misma interfaz pública que HttpRequest pero no está vinculada a ASP.NET. No requiere un pipeline web ejecutándose, ni nada de nada: es un simple contenedor de datos.

Por supuesto ASP.NET está construido alrededor de HttpRequest, lo cual sigue dificultando mucho los tests unitarios… ¿así que entonces? Bueno ASP.NET arrastra toda una historia y no es fácil (ni sensato) romper con todo, así que lo máximo que podemos exigir es que lo más nuevo sí se haga bien. Y lo más nuevo es ASP.NET MVC. En efecto, ASP.NET MVC está construido alrededor de HttpRequestBase y no de HttpRequest.

P.ej. se puede acceder a la request desde un controlador:

var queryString = ControllerContext.HttpContext.Request.QueryString;

Pero si observáis con detalle veréis que la propiedad Request no es de tipo HttpRequest si no de tipo HttpRequestBase:

image

¿Y como podemos usar esto en nuestros tests unitarios?

Imaginad un controlador que tenga el siguiente código:

public ActionResult About()

{

    var queryString = ControllerContext.HttpContext.Request.QueryString;

    var modelo = new FooModel();

    modelo.Nombre = ControllerContext.HttpContext.Request.QueryString["p1"];

    return View(modelo);

}

El controlador recoge el valor del parámetro “p1” de la querystring y establece la propiedad Nombre del viewmodel con este valor (Nota: ¡Este código es solo para demostrar lo que se comenta en este post, en ASP.NET MVC hay maneras mejores de hacer esto!)

Ahora vamos a ver como sería un test unitario que probase este método. Para ello… y aquí es donde entra en juego HttpRequestBase: nos permite crear un mock o un fake de ella:

public class HttpRequestFake : HttpRequestBase

{

    public override NameValueCollection QueryString

    {

        get           

        {

            var values = new NameValueCollection();

            values.Add("p1", "v1");

            return values;

        }

    }

}

Ya tenemos nuestro fake de HttpRequestBase para que devuelva una querystring fijada por nosotros (donde el parámetro p1 valga v1).

Ahora nos toca hacer un par de fakes más: de HttpContextBase (para que devuelva nuestro objeto Request) y de ControllerContext para que nos devuelva el fake de HttpContextBase:

public class HttpContextFake : HttpContextBase

{

    public override HttpRequestBase Request

    {

        get

        {

            return new HttpRequestFake();

        }

    }

}

public class ControllerContextFake : ControllerContext

{

    public ControllerContextFake()

    {

        HttpContext = new HttpContextFake();

    }

 

    public override HttpContextBase HttpContext { get; set; }

}

Y finalmente ya podemos tener nuestro test unitario:

[TestMethod]

public void TestMethod1()

{

    var controller = new HomeController();

    controller.ControllerContext = new ControllerContextFake();

    var result = controller.About() as ViewResult;

 

    Assert.AreEqual("v1", (result.ViewData.Model as FooModel).Nombre);

}

Y listos! Con esto comprobamos que el controlador hace lo que se supone que debe hacer (asignar el valor del parámetro p1 de la querystring a la propiedad Nombre del viewmodel).

Gracias al hecho de que HttpRequestBase y HttpContextBase NO están ligadas a ningún pipeline de ASP.NET y de que ASP.NET MVC ha sido construído en torno a ellas y no a las reales HttpRequest y HttpContext nos es mucho más fácil la realización de tests unitarios.

Espero que os haya sido de interés! ;-)
Un saludo!

Ah sí… y HttpRequestWrapper? Pues HttpRequestWrapper no es nada más que una clase que deriva de HttpRequestBase y que sirve para… convertir un objeto HttpRequest en un HttpRequestBase… ya, probablemente un nombre mejor para ella hubiese sido HttpRequestAdapter. ;-)

con no comments
Archivado en: