Pruebas unitarias: Servicios Web

Siguiendo con los post sobre pruebas unitarias, en éste intentaré explicar los recursos que nos ofrece el framework de Visual Studio para probar servicios web.

El primer paso será convertir nuestro método Sumar en un servicio web ASP.NET.

Como seguro que todos ya sabréis el proceso es muy sencillo. Sería crear un proyecto de tipo “ASP.NET Web Service Application” y decorar el método que queremos exponer con el atributo WebMethod.

[WebMethod]
public int Sumar(int a, int b)
{
    return a + b;
}

 

Una vez que tenemos el servicio web creado, desde el menú contextual crearemos el proyecto de Test y la prueba unitaria del método Sumar.

El código que nos genera Visual Studio es el siguiente:

[TestMethod()]
[HostType("ASP.NET")]
[AspNetDevelopmentServerHost("C:\WebService1\WebService1", "/")]
[UrlToTest("http://localhost:51038/")]
public void SumarTest()
{
    Service1 target = new Service1(); // TODO: Initialize to an appropriate value
    int a = 0; // TODO: Initialize to an appropriate value
    int b = 0; // TODO: Initialize to an appropriate value
    int expected = 0; // TODO: Initialize to an appropriate value
    int actual;
    actual = target.Sumar(a, b);
    Assert.AreEqual(expected, actual);
    Assert.Inconclusive("Verify the correctness of this test method.");
}

 

Como podéis ver, el código resultante es similar al que nos generó cuando el método Sumar no era un servicio web. En este caso ha añadido varios atributos al método, además del ya conocido TestMethod; HostType, AspNetDevelopmentServerHost y UrlToTest.

Pues bien, estos tres atributos nuevos no nos van a valer para mucho, ya que son atributos que se usan para probar interfaces ASP.NET pero no servicios web. Si ejecutásemos nuestra prueba unitaria fallaría y daría un error como este:

image

Si quitamos estos atributos el código resultante es el mismo que el que teníamos antes, cuando el método Sumar no era un servicio web. Y esto tiene su lógica, ya que un servicio web ASP.NET no es más que una clase normal y corriente en la cuál decoramos sus métodos con el atributo WebMethod.

Por lo tanto, probar la lógica que pueda incluir un servicio web sería como probar cualquier otra clase y no tendrá ninguna otra peculiaridad….o sí?

[TestMethod()]
public void SumarTest()
{
    Service1 target = new Service1();
    int a = 1;
    int b = 1;
    int expected = 2;
    int actual;
    actual = target.Sumar(a, b);
    Assert.AreEqual(expected, actual);
}

Pues sí, tiene una peculiaridad. Los clientes reales no acceden al servicio web como una clase normal, sino que acceden a ella a través de una clase proxy que se genera previamente. ( Add Web Reference o Add Service Reference ).

Por lo tanto, al menos una de las pruebas que implementemos sobre el servicio web debe simular ser un cliente real y hacer las llamadas a través de una clase proxy, para comprobar de esta manera que el servicio web se podrá exponer correctamente y que los clientes podrán llamarlo.

En el proyecto de Test tendremos que añadir una referencia web ( Add Web Reference ) al servicio web ASP.NET que queremos probar.

Una vez que tenemos la referencia, en lugar de llamar directamente a la clase, usaremos la clase proxy que se ha generado al añadir la referencia web.

Pero claro, si queremos acceder al servicio web a través del proxy, necesitaremos que el servicio se arranque cada vez que lanzamos la prueba. Si ejecutamos la prueba y el servicio no está arrancado, ésta fallará, ya que lógicamente no podrá llamarlo.

image

Para conseguir que el servicio web se arranque al ejecutar la prueba tendremos que usar el atributo AspNetDevelopmentServer y el método TryUrlRedirection.

[TestMethod()]
[AspNetDevelopmentServer("MiServicio","C:\WebService1\WebService1")]
public void SumarTest()
{
    MiServicio.Service1 target = new MiServicio.Service1();

    Assert.IsTrue(
        WebServiceHelper.TryUrlRedirection(target, testContextInstance, "MiServicio"),
        "Web Service Redirection Failed"
        );
    int a = 1;
    int b = 1;
    int expected = 2;
    int actual;
    actual = target.Sumar(a, b);
    Assert.AreEqual(expected, actual);
}

AspNetDevelopmentServer permite que creemos un servidor de desarrollo para el servicio, servidor que se arrancará en el momento que ejecutemos la prueba. Este servidor será temporal y finalizará al terminar la prueba. El servidor que se crea establece el puerto de escucha de forma dinámica lo que podría hacer que la prueba fallase, al no coincidir la URL que se ha creado en el servidor con la URL que está establecida en el fichero de configuración. Para hacer que la URL que se crea coincida con la que tenemos configurada se usa el método TryUrlRedirection.

TryUrlRedirecion es un método estático de la clase WebServiceHelper que devuelve un valor booleano si se ha podido ejecutar la redirección. Esté método recibe como primer parámetro el objeto del servicio web que vamos a redirigir.

Si utilizáis Visual Studio 2008 al añadir la referencia al servicio web también podéis usar “Add Service Reference” para que sea compatible con los servicios WCF. Si en lugar de estar probando un servicio web ASP.NET estáis probando un servicio WCF esta sería la única opción.

En este caso, si usáis la opción “Add Service Reference” notaréis que si intentáis usar el método TryUrlRedirection os dará un error de tipo en los parámetros, ya que este método no se puede usar con las clases proxy generadas de esta manera.

La solución es implementar nuestro propio método TryUrlRedirection, tal y como podemos ver en el siguiente código.

public bool TryUrlRedirection(object client, TestContext context, string identifier)
{
    bool result = true;
    try
    {
        PropertyInfo property = client.GetType().GetProperty("Endpoint");
        string webServer = context.Properties[
            string.Format("AspNetDevelopmentServer.{0}", identifier)].ToString();
        Uri webServerUri = new Uri(webServer);
        ServiceEndpoint endpoint = (ServiceEndpoint)property.GetValue(client, null);
        string serviceAddress = endpoint.Address.Uri.OriginalString;
        serviceAddress = serviceAddress.Replace(
            endpoint.Address.Uri.Authority, webServerUri.Authority);
        endpoint.Address = new EndpointAddress(serviceAddress);
    }
    catch (Exception)
    {
        result = false;
    }
    return result;
}

Para los que uséis servicio WCF lo único que hay que tener en cuenta es que si el proyecto dónde se alberga el servicio es una librería de servicios WCF no será necesario realizar ninguna acción adicional para conseguir que el servicio esté a la escucha en el momento de ejecutar la prueba. Este tipo de proyectos hace uso del host de servicio de WCF (WcfSvcHost) para poder hospedar de manera automática el servicio.

Si el servicio está en un proyecto web de WCF este proceso no se realizará de manera automática y habrá que realizar los mismo que para probar servicios web ASP.NET.

Ibon Landa

bon Landa lleva más de 15 años dedicado al desarrollo de software. Durante este tiempo ha trabajado en diferentes empresas en las cuáles ha podido trabajar en diferentes entornos y tecnologías. Actualmente está focalizado principalmente en tareas de desarrollo, arquitectura, en las herramientas del ciclo de vida y en todo lo relacionado con la plataforma de Cloud Computing Microsoft Azure, área en el que ha sido reconocido como MVP. Participa de forma activa en la comunidad, escribiendo su blog, manteniendo un portal sobre Microsoft Azure y colaborando con Microsoft y grupos de usuarios en eventos de formación, talleres y giras de producto.

Una respuesta a “Pruebas unitarias: Servicios Web”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *