19/1/2011 17:16 El Bruno

[VS2010] “Hola Mundo” con Moles

image47dd1de4

Buenas,

hace un tiempo escribí un post con un ejemplo sobre como utilizar Moles para realizar pruebas unitarias de Tasks en MSBuild. Hoy como veo que Luis está por empezar a escribir una serie de posts de Moles y SharePoint, pues voy a terminar este post que tengo en los drafts desde hace un tiempo. Primero lo primero, Luis ya ha explicado qué es un Mock, qué es un Stub, desde donde se descarga Moles, etc.; así que vamos al trapo.

Esta clase me la he encontrado hace muy poco en un proyecto heredado, como pueden ver la utilidad de la misma es monstruosa, pero bueno la he limpiado un poco para que nos sirva para el post:

   1: using System;
   2: using System.Diagnostics;
   3:  
   4: namespace ElBruno
   5: {
   6:     public class Log
   7:     {
   8:         public void Write(string message)
   9:         {
  10:             if(string.IsNullOrWhiteSpace(message))
  11:                 throw  new ArgumentException("invalid message");
  12:             var traceListener = new ConsoleTraceListener();
  13:             traceListener.WriteLine(message);
  14:         }
  15:     }
  16: }

Esta clase es muy útil y si tenemos bien configurados los Listeners en la configuración de la aplicación, pues funcionará muy bien, pero el problema surge si queremos probar la misma. Ojo, que este caso es casi un “probar por probar” que solo sirve para subir la cobertura de código, si quieres ver un poco una forma inteligente de comprender como trabajar con la cobertura de código, pues @Rodrigo lo explica muy bien en este post.

Pues bien, supongamos que tenemos configurado los Trace Listeners para que escriban en un archivo de texto, si comenzamos a implementar el siguiente test, pues a partir de la línea 7 comenzaremos a plantearnos opciones para leer el archivo  de texto.

   1: [TestMethod]
   2: public void TestWriteLogWithSimpleString()
   3: {
   4:     const string message = "Valentino";
   5:     var log = new Log();
   6:     log.Write(message);
   7:     // ¿¿??¿¿??
   8: }
   9: [TestMethod, ExpectedException(typeof(ArgumentException))]
  10: public void TestWriteLogWithEmptyStringThrowArgumentException()
  11: {
  12:     const string message = "";
  13:     var log = new Log();
  14:     log.Write(message);
  15: }

Si luego cambiamos la configuración del Listener para que escriba en una cola de mensajes, en la consola, etc; pues la cosa se nos complica ya que nos alejamos de un test simple y rápido. Aquí es donde entra en juego nuestro amigo Moles, ya que con el mismo podemos crear un Mock que reemplace al Trace Listener original e implemente uno propio para nuestras pruebas.

Lo primero que deberemos hacer, es en el proyecto de pruebas en Visual Studio 2010, seleccionar el ensamblado sobre el que queremos trabajar, desplegar el menú contextual y seleccionar la opción [Add Moles Assembly]. En este caso y como no le tenemos miedo a nada estamos trabajando con Listeners, pues tenemos que generar un Moles del ensamblado System. Este proceso tarda unos segundos, y una ves finalizado el mismo, podremos ver un nuevo ensamblado en nuestra lista de referencia [System.Moles] que es el que permite trabajar con Mocks de System.

image

Ok, el primer paso está dado, ya hemos generado nuestros Moles ahora tenemos que trabajar con los mismos. Y es en este punto, donde comenzaremos a ver que el trabajar con pruebas unitarias, además de darnos un contrato de trabajo cerrado sobre las funcionalidades de nuestras aplicaciones, nos ayuda a implementar un diseño ágil que no acople elementos.

Por ejemplo, en nuestra clase Log tenemos que implementar un mecanismo que nos permita decidir el Listener con el que queremos trabajar. Puede ser el propio de System, o el creado por Moles para nuestros tests. Esto se logra desde los tiempos de mi abuela definiendo en el constructor los argumentos de trabajo (aquí tengo que recordarte los principios SOLID de diseño de objetos para que tampoco crees clases con constructores con 50 parámetros). Para este ejemplo rápido, nuestra clase puede quedar similar al siguiente código:

   1: using System;
   2: using System.Diagnostics;
   3:  
   4: namespace ElBruno
   5: {
   6:     public class Log
   7:     {
   8:         private readonly TraceListener _traceListener;
   9:         public Log()
  10:         {
  11:             _traceListener = new ConsoleTraceListener();
  12:         }
  13:         public Log(TraceListener traceListener)
  14:         {
  15:             _traceListener = traceListener;
  16:         }
  17:         public void Write(string message)
  18:         {
  19:             if(string.IsNullOrWhiteSpace(message))
  20:                 throw  new ArgumentException("invalid message");
  21:             _traceListener.WriteLine(message);
  22:         }
  23:     }
  24: }

 

Si volvemos a nuestro test podremos ver que en el mismo ya tenemos la capacidad de trabajar con los namespaces MOLES dentro de System. Además para cada tipo se ha creado uno nuevo con un prefijo “M” que es el tipo que intercepta y crea MOLES, por ejemplo:

image

Otro punto a tener en cuenta es que para cada uno de los métodos y sobrecargas que existen en la clase original, Moles crea una propiedad específica con la sobrecarga que nos permite implementar un delegado para la utilización de la misma.

image

 

Jo, lo he leído y suena a chino, mejor vamos al código que es lo que mejor se entiende:

   1: [TestMethod]
   2: public void TestWriteLogWithSimpleString()
   3: {
   4:     const string message = "Valentino";
   5:     var defaultTraceListener = new MDefaultTraceListener();
   6:     defaultTraceListener.BehaveAsDefaultValue();
   7:     defaultTraceListener.WriteString = (s) =>
   8:                          {
   9:                              Assert.AreEqual(message, s);
  10:                          };
  11:     var log = new Log(defaultTraceListener);
  12:     log.Write(message);
  13: }

 

Listo !!! no hace falta que explique el código, pero si quieres hay van un par de puntos a tener en cuenta:

  • En la línea 5 se crea el Listener implementado por MOLES
  • En la línea 6, se define que el comportamiento será el por defecto para este Listener salvo que se indique lo contrario
  • En la línea 7 se define el comportamiento de la función Write() y en la misma se evalúa el test para darle sentido al mismo
  • En la línea 11 se utiliza la clase de Log, pero con el constructor específico donde se le pasa la instancia de TraceListener

Más simple imposible … ahora quedo a la espera de los de SharePoint !!!

 

Saludos @ Rio IV

El Bruno

   

Archivado en: ,,,,
Comparte este post:

# re: [VS2010] “Hola Mundo” con Moles

Wednesday, January 19, 2011 7:39 PM by Luis Ruiz Pavón

Hola Bruno,

Lo primero gracias por la mención :)

Quería comentar una cosilla:

En este caso concreto puedes usar Stubs para probar el código, puesto que la clase que quieres testear no está sellada, no tiene métodos estáticos...

[TestMethod]

       public void TestWriteLogWithSimpleString()

       {

           const string message = "Valentino";

           var defaultTraceListener = new SDefaultTraceListener

                                          {

                                              WriteLineString = (s) => Assert.AreEqual(message, s)

                                          };

           var log = new Log(defaultTraceListener);

           log.Write(message);

       }

A ver si termino de preparar los vídeos sobre Moles con métodos no deterministas y SharePoint ;)

Un saludo y buen post!

# re: [VS2010] “Hola Mundo” con Moles

Thursday, January 20, 2011 1:05 AM by El Bruno

@Luis, thanks por el dato :D

ReSharper me avisó del cambio, pero pasé de hacerle caso jejeje

Salu2