Invocando dinámicamente un método genérico

No sé si es debido a un bug o simplemente es algo a lo que no hay modo sencillo de dar soporte, pero el caso es que los accessors que podemos utilizar en los proyectos de testing en Visual Studio 2008, no son capaces de invocar métodos que sean privados, estáticos y genéricos (a la vez).

Una posible solución (desconozco si hay más y más sencillas) es la invocación de estos métodos en nuestros tests mediante Reflection. Con este tipo de fragmentos de código vuelves una y otra vez a por ellos según vas pasando de proyecto en proyecto, aquí queda para el futuro.

 

   1:  // Obtener el MethodInfo asociado al metodo a invocar
   2:  MethodInfo methodInfo = typeof(ClassToInvoke).GetMethod(
   3:      "MethodToInvoke", System.Reflection.BindingFlags.Static | BindingFlags.Public);
   4:   
   5:  // Bindear el tipo con el metodo que queremos invocar
   6:  Type[] genericArguments = new Type[] { genericType };
   7:  MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
   8:   
   9:  // Invocar el metodo y pasarle los parametros necesarios
  10:  // El parametro null es el objeto desde el que se invoca el metodo. 
  11:  // Al ser un estatico, debe ser null
  12:  try
  13:  {
  14:      genericMethodInfo.Invoke(null, new object[] { value1, value2 });
  15:  }
  16:  catch (TargetInvocationException ex)
  17:  {
  18:      // Cualquier excepción lanzada por el método irá wrappeada dentro 
  19:      // de una TargetInvocationException
  20:      if (ex.InnerException != null)
  21:      {
  22:          throw ex.InnerException;
  23:      }
  24:      else
  25:      {
  26:          throw;
  27:      }
  28:  }

Testeo unitario con Moles

Moles es una nueva virguería de la gente de Microsoft Research; en concreto, del equipo de desarrollo de PEX, una herramienta de generación de tests white-box, para quien no lo conozca.

¿Qué es Moles? Un nuevo framework que permite sustituir cualquier método de .NET por un delegado. ¿Y qué podemos hacer con esto? Pues sustituir cualquier llamada al framework por lo que queramos que se ejecute. Esto, evidentemente, tiene especial utilidad en el testeo unitario, donde necesitamos crear las condiciones adecuadas para ver cómo se comporta nuestro código. Un ejemplo sencillo, expuesto en este vídeo por uno de sus creadores, sería poder “trucar” la hora que nos devuelve DateTime.Now. Como sabemos, no se puede modificar pero… como Moles ¡sí! Si quieres saber cómo, échale un vistazo al vídeo.

Yo voy a proponer un ejemplo distinto: acceder a las appSettings del ConfigurationManager. En el pasado tuve que modificar la signatura de un método que leía, internalmente, una propiedad de appSettings, para poder testearlo. La razón es que no tenía manera de simular las distintas condiciones (ausencia de la clave buscada, valor malformado, etc) puesto que la clase ConfigurationManager no podía mockearse. La solución al final fue pasarle al método la NameValueCollection que contiene las appSettings, pudiendo así simular mis condiciones de testeo.

Pero no mola  tener que modificar un método que ya tienes codificado por culpa de las limitaciones del framework de testeo. Aquí es donde Moles va a echarnos una mano. Veamos paso a paso cómo.

Empecemos con el método que vamos a testear. Digamos que lee una variable de las appSettings y la retorna. Si no existe la clave o tiene un valor vacío, lanza una excepcion. El código podría ser algo así:

/// <summary>
/// Retrieves app setting by its key.
/// </summary>
/// <returns>Value in the settings.</returns>
public static string ExtractAppSetting()
{
    string key = "myKey";
    if (ConfigurationManager.AppSettings.AllKeys.Contains(key) == false ||
        string.IsNullOrEmpty(ConfigurationManager.AppSettings[key]) == true)
    {
        throw new ConfigurationErrorsException("Key not found");
    }

    return ConfigurationManager.AppSettings[key];
}

Fácil, ¿no? El test más típico para este código sería aquél en el que previamente hemos configurado una key en la sección AppSettings del fichero App.config y después intentamos recuperarla mediante el método testeado. Suponiendo la existencia de una setting con key “myKey” y valor “myValue”, el siguiente test nos daría un bonito verde.

/// <summary>
/// Test that checks if the method returns the 
/// appropriate value from AppSettings.
/// </summary>
[TestMethod]
public void ExtractAppSettingValidConfigurationOk()
{
    Assert.AreEqual<string>(
        "myvalue", 
        AppSettingsManager.ExtractAppSetting(), 
        "Invalid value in AppSettings");
}

Bien, ¿y cómo podemos probar ahora el comportamiento de nuestro método si no existiera la key? Quizás, a la desesperada, nos podríamos plantear intentar modificar por código el fichero app.config. Sin embargo, esto no sería una buena idea porque los tests, de tener varios, se lanzan en hilos en paralelo, por lo que podríamos tener resultados inesperados si modificamos este fichero: las clásicas condiciones de carrera.

La solución nos la aporta Moles. En primer lugar, añadimos una referencia al assembly que contiene la clase ConfigurationManager; es System.Configuration. Después, ya podemos añadir el fichero Moles a nuestro proyecto de testeo, como muestra la siguiente figura. El nombre que le daremos debería coincidir con el assembly que vamos a “molear” (¡toma palabro!).

AddingMolesFile

Una vez creado el fichero Moles, ya podemos empezar a usar su “magia” en nuestro código de testeo. Creamos otro test, pero esta vez probará que el método lanza una excepción si las AppSettings están vacías y, por tanto, no contienen la key que estamos buscando. ¿Cómo se haría esto? Sencillo, usando el mole del ConfigurationManager, que nos dará la posibilidad de modificar la colección AppSettings.

/// <summary>
/// Test that checks if the method throws an exception if 
/// the AppSettings is empty.
/// </summary>
[TestMethod]
[ExpectedException(typeof(ConfigurationErrorsException))]
public void ExtractAppSettingEmptySettingsExceptionExpected()
{
    MConfigurationManager.AppSettingsGet = () => new NameValueCollection();
    AppSettingsManager.ExtractAppSetting();
}

Aún nos quedaría un último paso, puesto que si lanzáramos el test ahora mismo nos daría una excepción en tiempo de ejecución indicándonos que tenemos que añadir el siguiente atributo para indicar que el test tiene que ejecutarse “hosteado” por Moles. El test quedaría así finalmente:

/// <summary>
/// Test that checks if the method throws an exception if 
/// the AppSettings is empty.
/// </summary>
[TestMethod]
[HostType("Moles")]
[ExpectedException(typeof(ConfigurationErrorsException))]
public void ExtractAppSettingEmptySettingsExceptionExpected()
{
    MConfigurationManager.AppSettingsGet = () => new NameValueCollection();
    AppSettingsManager.ExtractAppSetting();
}

Y éste sería el resultado:

ExceptionFromMoles

¿Cómo? ¿Una excepción en tiempo de ejecución? Pues sí, resulta que Moles no soporta la modificación del objeto System.Configuration.ConfigurationManager. En este hilo lo explica levemente uno de sus creadores. Así que en lugar de tirar la basura el post, he preferido indicar un ejemplo de su uso, aunque el final haya sido tan trágico como el de Seven.

Otro día prometo postear un resultado correcto. En cualquier caso, Moles sigue siendo una tecnología a seguir, aunque con sus limitaciones.

 

Bibliografía:

Descargas:

WCF4 – Configuración simplificada

Creo que muchos de los que hemos trabajado con Windows Communication Foundation podemos estar de acuerdo en que, en mayor o menor medida, no resulta una tecnología fácil de configurar. Es cierto que aporta una gran flexibilidad en el desarrollo, pero a la hora de hacer el despliegue hay que ser cuidado con el web.config/app.config de turno, si no queremos llorar.

Seguramente esto ya había sido identificado por Microsoft como punto negro del framework, pues muchas de las mejoras que para el lanzamiento de .NET 4 va a haber respecto a WCF, se centran en simplificar dicha configuración, tirando sobretodo de valores por defecto.

En el próximo post vamos a ver las diferencias que habría entre configurar un servicio “típico” de WCF en 3.5 y hacerlo en 4.0.

Hola Mundo en 3.5

Vamos a empezar por hacernos un servicio sencillo con .NET 3.5 y Visual Studio 2008. No me detendré mucho en ello, porque el proceso es bien conocido por todos. Empezamos por el contrato de servicio:

/// <summary>
/// Service that says hello world.
/// </summary>
[ServiceContract]
public interface IHelloWorld
{
    /// <summary>
    /// Gets "Hello Word from X", where X is the name parameter.
    /// </summary>
    /// <param name="name">Who says hello.</param>
    /// <returns>String with hello world message.</returns>
    [OperationContract]
    string GetHelloWord(string name);
}

La implementación para este servicio podría ser algo como lo siguiente:

/// <summary>
/// Implementation of the IHelloWorld service contract.
/// </summary>
public class HelloWorld : IHelloWorld
{
    /// <summary>
    /// Gets "Hello Word from X", where X is the name parameter.
    /// </summary>
    /// <param name="name">Who says hello.</param>
    /// <returns>String with hello world message.</returns>
    public string GetHelloWord(string name)
    {
        return string.Format("Hello world from {0} - Request from {1}", name, OperationContext.Current.Channel.LocalAddress);
    }
}

Por último, lo que realmente nos ocupa en este post, la configuración. Digamos que necesitamos desplegar este servicio de forma que pueda ser consumido mediante un binding lo más interoperable posible (BasicHttpBinding es nuestro hombre) y otro que sea más específico para aplicaciones .NET y que nos dé más rendimiento (vamos a tirar por NetTcpBinding). Además, todavía necesitamos información sobre errores que haya en el servidor, puesto que no está totalmente terminado, y no nos vendría mal un punto MEX para poder crear nuestros proxies.

<system.serviceModel>
  <services>
    <service behaviorConfiguration="HelloWordService.ServiceBehavior"
             name="HelloWordService.HelloWorld">
      <!-- Binding para peticiones HTTP -->
      <endpoint address=""
                binding="basicHttpBinding"
                contract="HelloWordService.IHelloWorld" />
      <!-- Binding para peticiones TCP -->
      <endpoint address=""
                binding="netTcpBinding"
                contract="HelloWordService.IHelloWorld" />
      <!-- Binding para obtener metadatos -->
      <endpoint address="mex"
                binding="mexHttpBinding"
                contract="IMetadataExchange" />
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:9999/HelloWorldService"/>
          <add baseAddress="net.tcp://localhost:9998/HelloWorldService"/>
        </baseAddresses>
      </host>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="HelloWordService.ServiceBehavior">
        <!-- Activar la generación de metadatos para el binding MEX -->
        <serviceMetadata httpGetEnabled="True"/>
        <!-- Incluir información detallada para excepciones -->
        <serviceDebug includeExceptionDetailInFaults="False" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

Con esta configuración tendríamos a nuestro servicio escuchando en las dos direcciones base definidas, una para TCP y otra para HTTP. También tendríamos funcionando un endpoint MEX para poder generar nuestros proxies y consultar el fichero WSDL. Este servicio podríamos desplegarlo bien en IIS o bien hostearlo mediante una aplicación de consola. El resultado sería parecido, con la salvedad de que las baseAddress no son tenidas en cuenta por IIS, sino que se toma la ruta en la que se haga el despliegue de la aplicación. Recordad que es necesario configurar los bindings del Site que aloja a la aplicación web para poder tener funcionando el endpoint sobre net.tcp.

Para terminar esta parte, si nos creáramos un cliente de este servicio en su versión hosteada en IIS, en la parte de la configuración podríamos ver un fragmento como éste:

<client>
    <endpoint address=http://localhost/HelloWorldService binding="basicHttpBinding"
        bindingConfiguration="BasicHttpBinding_IHelloWorld" contract="RemoteServices.IHelloWorld"
        name="BasicHttpBinding_IHelloWorld" />
    <endpoint address="net.tcp://localhost/HelloWorldService"
        binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IHelloWorld"
        contract="RemoteServices.IHelloWorld" name="NetTcpBinding_IHelloWorld">
        <identity>
            <userPrincipalName value=j.holguera@xxx.local />
        </identity>
    </endpoint>
</client>

Con estos dos endpoints definidos, podríamos consumir el servicio mediante los dos protocolos (HTTP y TCP), como muestra la siguiente imagen.

using (RemoteServices.HelloWorldClient proxy = new RemoteServices.HelloWorldClient("BasicHttpBinding_IHelloWorld"))
{
    Console.WriteLine("HelloWorld using HTTP: " + proxy.GetHelloWord("Javier"));
}

using (RemoteServices.HelloWorldClient proxy = new RemoteServices.HelloWorldClient("NetTcpBinding_IHelloWorld"))
{
    Console.WriteLine("HelloWorld using TCP: " + proxy.GetHelloWord("Javier"));
}

El resultado sería sendas cadenas de texto en las que, además de aparecer el mensaje de saludo, se nos indica la dirección desde la que se ha recibido la petición en el servicio, útil para ver que efectivamente consumimos la funcionalidad mediante los dos protocolos.

 ConsoleClient35

Como se puede ver, la única parte que realmente existe un buen  puñado de líneas es la configuración del servicio. Veamos cómo quedaría usando WCF 4.

Hola Mundo en WCF 4

Partamos de la base de que hemos creamos un proyecto WCF con Visual Studio 2010 y hemos replicado el servicio anterior, es decir, el contrato de servicio IHelloWorld y su implementación HelloWorld. Sólo nos quedaría, por tanto, configurar el servicio en el fichero app.config. Empezamos definiendo los bindings para HTTP y TCP. La configuración necesaria sería ésta:

<configuration>
</configuration>

¿No hay configuración? ¿Cómo es posible? Pues gracias a los Default Endpoints. Son endpoints “preconfigurados” que se cargan automáticamente para cada dirección base creada. Al llamarse al método Open del ServiceHost, ya sea automáticamente por parte de IIS o manualmente cuando se hostea el servicio en una aplicación de consola, se crean estos bindings predeterminados haciendo uso del método AddDefaultEndpoints. Un par de cuestiones a tener en cuenta:

  • Si se configura un endpoint en el fichero de configuración, ya no se hace efectiva la llamada a AddDefaultEndpoints.
  • Si aún así quisiéramos tener esos endpoints por defecto, siempre es posible llamar explícitamente al método AddDefaultEndpoints y añadir, a los endpoints definidos en el fichero de configuración, los que crea él por defecto.

Los bindings traen una configuración por defecto que se considera como más habitual. Por ejemplo, con una dirección base con protocolo http, el binding por defecto es basicHttpBinding. Esta nueva técnica ha sido bautizada como Default Protocol Mapping. En cualquier caso, este mapeo también es configurable; si quisiéramos que, por defecto, la direcciones con protocolo se resolvieran con un binding de tipo wsHttpBinding, sería necesario configurar lo siguiente en el app.config/web.config:

  <system.serviceModel>
    <protocolMapping>
      <add scheme="http" binding="wsHttpBinding"/>
    </protocolMapping>
  </system.serviceModel>

En este punto, tendríamos nuestro servicio hosteado en IIS. Nos quedaría activar los metadatos del servicio para generar el fichero WSDL y enviar información detallada en caso de error. Ambas no son configuraciones por defecto del servicio, por lo que tendríamos que editar el fichero app.config/web.config e introducir lo siguiente:

  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Si nos fijamos, veremos que no ha sido necesario ni darle un nombre al nuevo behavior del servicio, ni tampoco definir el servicio y asociarle el behavior, como hacíamos en 3.5. Esto se conoce como Default Behavior Configurations, configuraciones sin nombre que se van a aplicar a cualquier servicio que definamos. Tienen su contrapartida en los Default Binding Configurations, que realizan la misma función pero asociados a un tipo de binding.

 

Como se ha podido ver a lo largo del post, la configuración en WCF4, al menos la más habitual, se ha simplificado enormemente. Los nostálgicos de la sencillez de los servicios ASMX han perdido la principal razón para no dar el paso a WCF.

 

Bibliografía:

A Developer’s Introduction to Windows Communication Foundation 4

Descargas:

Debugger in Silverlight

Este fin de semana perdí una cantidad considerable de tiempo porque no me funcionaba el debugger en Silverlight. Básicamente, me salían esos simpáticos mensajes que aparece en Visual Studio cuando un breakpoint no se va a ejecutar:

The break point currently will not be hit , no symbols have been loaded to this document

¿Por qué? ¡¿Por qué?! Me preguntaba yo, intentando depurar una aplicación que estaba comportándose de forma incorrecta. Pues bien, la solución es tan sencilla, de esas que por suerte no se olvidan. Basta con activarlo dirigiéndose a las propiedades del proyecto ASP.NET que estará hosteando nuestro control Silverlight.

En las propiedades, bajo la pestaña Web, en la parte inferior de la pantalla se puede ver un menú como el de la siguiente imagen. Basta activar el checkbox correspondiente a Silverlight para que nuestro depurador favorito funcione de nuevo correctamente.

SilverlightDebugger

Espero que esto le sirva a alguien para perder menos tiempo del que perdí yo.

Code Coverage (II): Uso

Una vez que tenemos activado el Code Coverage para un ensamblado y tras lanzar los correspondientes tests, ver los resultados es una mera cuestión de abrir la pestaña de “Code Coverage Results”, que si no es visible se puede activar en el menú “Test – Windows – Code Coverage Results”.

CodeCoverageResults

En la ventana de ejemplo se puede ver cómo los resultados se gestionan, en primer lugar, por ensamblado ; y dentro del ensamblado , cómo se pueden ir expandiendo los diferentes espacios de nombres, clases y, por último, métodos. Sin duda, una gran granularidad que nos permite saber con precisión dónde estamos quedándonos cortos en el testeo.

Pero, si esta ayuda es buena, tener el código coloreado indicando qué partes han sido ejecutadas durante el testeo y cuáles no, sería aún mejor, ¿verdad? Basta con pulsar el botón “Show Code Coverage Coloring”, el tercero por la derecha en la ventana de “Code Coverage Results”, para obtener algo como la siguiente imagen.

ColoredCode

¡Genial! Ahora no sólo sé la cantidad de código que me falta por testear, sino que incluso sé qué para qué código necesito escribir nuevos tests. Vale, esto se salta totalmente los principios de TDD, pero la vida no siempre es maravillosa…

Un último apunte para terminar: he comprobado (en mis carnes) que algunos proyectos dan problemas al intentar configurar los ensamblados a los que se les va a calcular la cobertura de código.

Los síntomas son sencillos: abrimos el .testrunconfig, pinchamos en “Code Coverage”, y la ventana se cierra sin emitir ningún error. No soy el primero al que le pasa y aunque está reportado como bug, aparentemente sigue sin solucionarse.

La forma de evitarlo es tan chapucera como útil: simplemente hay que hacer el unload de algunos proyectos que son los que dan problemas. A mí, personalmente, me ha ocurrido exclusivamente con los proyectos de base de datos, pero parece ser que también existen problemas con proyectos WSSF. Avisados quedáis.

Code Coverage (I): Activación

Voy a decirlo aquí en alto y con vergüenza: hasta esta semana, no había usado nunca el Code Coverage. Y como ocurre con todas las buenas herramientas, ahora pienso: “¿cómo he podido estar sin esto hasta ahora?”.

Empezar a usarlo es sencillo, aunque tiene una pequeña peculiaridad: sólo funciona cuando los tests se ejecutan, no si se depurar. ¿Qué quiere decir esto? Que a la hora de seleccionar el conjunto de tests que queramos lanzar, deberemos utilizar la opción “Run Selection” en lugar de “Debug Selection”, en la ventana de “Test View”.

TestViewWindow

Como ya sabemos, el problema de esto es que cualquier punto de depuración que tengamos en el código no va a ejecutarse, pero parece un mal menor comparado con las posibilidades que tiene Code Coverage. Además, uno de los principios de testeo unitario es precisamente evitar la necesidad de depurar el código, así que no hay de qué quejarse.

Para poder ver la pestaña que muestra los resultados de cobertura de código existen dos posibilidades: pulsar el último de los botones que parecen en la ventana de “Test Results” o bien acceder a la opción de menú “Test – Windows – Code Coverage Results”.

Sin embargo, de poco nos servirá esto si no activamos previamente el Code Coverage para que se calculen sus resultados. Esto se configura en el fichero .testrunconfig que existe en la carpeta “Solution Items”.

TestRunConfig

Una vez abierto, hay pulsar en la opción de “Code Coverage” y seleccionar los assemblies sobre los que vamos a calcular la cobertura de código. Este punto es más importante de lo que podría pensarse, puesto que lo normal será tener dos versiones para cada uno: la versión de Debug y la versión de Release (podemos tener más si existen más configuraciones del proyecto). La clave es que no podemos elegir las dos a la vez, por lo que seleccionemos la que seleccionemos, tendrá que estar “sincronizada” con la configuración actual del proyecto. De lo contrario, tendremos un problema: la cobertura se calculará sobre un assembly que no tiene porqué estar actualizado con los últimos cambios que hemos hecho en el código.

Un ejemplo: digamos que hemos elegido la versión de Debug del assembly, pero tenemos configurado el proyecto en modo Release. Tenemos un 100% de cobertura de código, pero introducimos nuevas líneas en un método y compilamos (en Release). El assembly de Debug, sobre el que se va a pasar la cobertura, no ha cambiado, por lo que nos seguirá marcando 100% de cobertura aún cuando sabemos que no tenemos tests para el código que acabamos de añadir. Puede ser todo un problema, ¿verdad?

Por hoy es suficiente. Otro día, cómo interpretar los resultados y el coloreado del código.

Añadir assemblies a Visual Studio

Muchas veces necesitamos que un assembly esté en el GAC para no estar preocupados de incluirlo en cada proyecto. Simplemente, lo seleccionamos de la ventana “Add Reference” de Visual Studio, y somos felices con ello. Sin embargo, esta semana pasada me surgió la necesidad de añadir un assembly al GAC por mí mismo.

AddReferenceWindow

No es que sea demasiado difícil, basta con copiarlo a c:windowsassembly. Hasta un tecnicoless podría hacerlo. Sin embargo, no apareció mágicamente en la ventana de Add Reference. ¿Qué había hecho mal? ¿Qué me faltaba? Buceando un poco por Internet encontré a un tipo explicando cómo hacerlo de 3 formas distintas.

Huelga decir que elegí la más sencilla: copiar mi assembly a C:Program FilesMicrosoft Visual Studio 9.0Common7IDEPublicAssemblies

Rearrancar el Visual Studio fue suficiente para ver mi assembly en la pestaña .NET. ¿Fácil no?