Páginas

19 sept. 2012

Mis montañas en 3D

Hoy leo que María Teresa Ruiz Monzón, una informática de la Universidad del País Vasco (UPV/EHU), acaba de presentar una aplicación para que los montañeros puedan usar la geolocalización 3D en sus móviles. Por lo que he leído, porque no he conseguido saber cómo se llama la aplicación para probarla, tiene un grave fallo, en mi opinión, y es que necesita de conexión a Internet para ir descargando los datos necesarios para representar el área que tienes a tu alrededor.  Lo he leído en éste artículo en el que también se dice que tiene problemas de memoria. Por las tecnologías que menciona y por la captura de pantalla que he podido ver de la aplicación creo que el desarrollo ha sido muy similar a "mis montañas en 3D" sólo que yo tomé la decisión de, en vez de posibilitar la reproducción de la zona de España en la que estés, por esos mismos problemas de memoria, reproducir con más calidad zonas montañosas concretas como el Circo de Gredos en Ávila, el Valle de Ordesa en Huesca, o distintas zonas de la Sierra de Guadarrama en Madrid y Segovia como la zona de Peñalara o la ruta de La Cuerda Larga que va desde el Puerto de Navacerrada hasta el Puerto de La Morcuera. 

A raíz de ésta noticia he decidido volver a ponerme manos a la obra con "mis montañas en 3D" porque tengo pendiente, casi desde que saqué la primera versión de Gredos 3D que fue la primera que hice y una de las más vistosas, varias mejoras que, curiosamente, si que ha desarrollado María Teresa. Una de ellas es el modo niebla, que convertiría "mis montañas 3D" más que en un juego en una ayuda en montaña ya que, gracias a la brújula y a los niveles del dispositivo, en caso de niebla nos mostraría el terreno que tenemos delante aunque no lo viéramos. Otra de las mejoras y con la que me pondré primero es, en modo juego, es decir, moviéndote libremente por "el escenario", dar al usuario información sobre su posición, su altitud y la dirección en la que mira. La tercera de las mejoras sería poder hacer zoom, sé que OpenGL ES lo permite y lo apliqué en uno de los tutoriales que seguí para aprender pero tendría que recordarlo. En resumen son tres puntos, mis tres próximos hitos:
  1. Dar información de tu posición en modo juego, coordenadas, altura y dirección.
  2. Posicionarte en el mapa si estás en ése área, modo niebla, y utilizar sensores para mostrar lo que se debería ver.
  3. Poder hacer zoom.
  4. (Añadido a posteriori) Mejorar la pantalla de inicio.

No voy a sacar más mapas en 3D hasta que "el motor 3D", que es el mismo en todas las aplicaciones, no tenga, al menos, dos de estas tres nuevas características aunque si que tengo pensado mejorar algunas cosas en todas ellas como las texturas de alguna de ellas. Voy a hacer ahora un resumen del "estado del arte" de "mis montañas en 3D". 

Todas mis aplicaciones Android las publicaré con mi nombre completo y son accesibles desde aquí.


La primera que subí fue Gredos 3D, que fue uno de los primeros intentos de aprender a programar Android y que acabó derivando en una interminable serie de horas "jugando" a programar cosas en 3D. Muy entretenido y para mi gusto con un resultado final espectacular, aunque claro, soy el autor y siempre veo fallos, no me gusta, ni en ésta ni en las otras, la pantalla inicial, creo que es una chapuza. En Gredos 3D estamos en una representación en 3D del Circo de Gredos, Ávila, y sus alrededores. Podemos pasear rápidamente hasta el pico Almanzor o la Galana, andar por la Laguna Grande de Gredos y acercarnos a Los Galayos.  Si no recuerdo mal, no tengo los datos delante, eran 15 kilómetros cuadrados.



La siguiente aplicación que subí fue una variación de Gredos 3D a la que llamé Gredos Virtual 3D en la que en vez de una textura realizada "artesanalmente" juntando ortofotos de muchísima calidad realicé, a partir del mapa de alturas, una textura que no era más que un mapa con curvas de nivel y sombras creadas "automáticamente" y en el que dibujé a mano las lagunas, la carretera de la Plataforma de Gredos y los refugios de El Reguero Llano y el Refugio Elola.


[ACTUALIZADO 04/10/2012]

Hay bastante gente que ya lo sabe porque según las estadísticas de la Consola del desarrollador de Google Play hubo149 instalaciones totales de usuarios y hay 27 instalaciones activas de dispositivos actualmente. PAra todos menos esas 27 personas: volví a crear la textura de Gredos 3D con unas curvas de nivel más estandarizadas, es decir, con una separación de 20m entre las curvas. Me he guardado la imagen base, el mapa con curvas de nivel, para ir añadiendo sobre él caminos, rutas, cascadas de hielo, etcétera y ya he encontrado la manera de ponerle una leyenda en 3D que pueda leer el observador desde cualquier ángulo pero aún no me voy a poner con eso. Quiero que veáis el "estado del arte" del modo niebla y creo qu el siguiente vídeo habla por sí solo.



Mi intención es acabarlo hoy, ya tengo abierto el eclipse y el proyecto y las ideas claras sobre cómo quiero hacer la interfaz de usuario mientras se obtiene la posición GPS. Prometo hacer lo mismo para la siguiente actualización de Ordesa Virtual 3D en cuanto acabe con Gredos Virtual 3D.

[/ACTUALIZADO 04/10/2012]


Ordesa Virtual 3D es una representación de 10 kilómetros cuadrados alrededor del abrupto Valle de Ordesa. Para quien conozca ésta zona puedo decir que aparece parte del Cañón de Añisclo, El Monte Perdido y el Cilindro, El Balcón de Pineta y parte del Circo de Gavarnié. Al igual que sucede con Gredos Virtual 3D el resultado de la textura no acaba de convencerme pues no es estándar, tengo que modificarlo para que quede como en la representación que, para mi gusto, mejor quedó, la de Peñalara 3D Map. Dejo unas capturas del estado actual de Ordesa Virtual 3D.


En éstas imágenes se aprecia la sombra y se ve que están marcadas las alturas de algunas curvas de nivel pero el "código de colores" aunque si que es bastante estándar no acaba de gustarme, por eso ésta textura también la voy a modificar para que se parezca a la de Peñalara 3D Map.


Ordesa 3D es el mismo escenario que el anterior pero con una textura basada en Ortofotos copiadas y pegadas en un lienzo de 2048x2048 px. Fue un trabajo duro, cansado para la vista, pero creo que me quedó bastante bien, el único problema fue que dicha textura en formato png ocupaba una barbaridad (más de 10 MB) así que la comprimí y la guardé en otro formato que llevaba menos ruido y datos que para lo que se necesita en la aplicación son inútiles. Ésta optimización vino por un comentario que me hizo un usuario de la aplicación ya que siempre dejo mi correo "profesional" a disposición de mis potenciales usuarios (d.jgc.it@gmail.com). Él me dijo que en su dispositivo la aplicación no funcionaba debido a un desbordamiento de la memoria. Ésto me llevo a desarrollar Ordesa 3D Little (mismo escenario pero mucha menos resolución en cuanto a alturas y en cuanto a texturas pero parecida definición en tiempo de ejecución) y a encontrar la manera en la que después he ido reduciendo el paso de las distintas texturas de "mis montañas 3D". Ordesa 3D y 3D Little se ven así:


Creo que cualquiera que haya estado por allí podrá decir que es tremendamente realista, en esas imágenes vemos distintas perspectivas del Valle de Ordesa (1, 5 y 6) el Glaciar de Monter Perdido (2 y 3) y el Balcón de Pineta desde el Monte Perdido hacia donde debería estar el Refugio de Tucarroya (4). Espero tener pronto el modo niebla y el panel con los datos de la posición y la dirección de la cámara en modo juego.


Ya he hablado antes de ésta aplicación y he dicho que era la que tenía la mejor textura (virtual o tipo mapa) de todas. Es un mapa virtual en 3D con curvas de nivel cada 20 metros alrededor del Pico Peñalara. Me gusta mucho como me quedó la textura y la manera en la que la hice y será como haga a partir de ahora todas las aplicaciones que pretendan ser un mapa topográfico en 3D, es decir, en un solo color, verdoso, con sombras y curvas de nivel cada 20 metros. En esta App aparecen marcadas varias zonas, en dos tonos más oscuros de verde, primero el parque de Peñalara y segundo las zonas de especial protección medioambiental del mismo; en tono azulado aparece la parte de la estación de esquí de Valdesquí que queda dentro del mapa. ¿Que os parecen éstas imágenes?


¿Os gusta? Mi opinión ya la sabéis, y es que todas las texturas de mapas virtuales en 3D deberían ser como ésta, mejora que aplicaré poco a poco tanto a Gredos Virtual 3D como a Ordesa Virtual 3D.


Ésta es la aplicación que abarca más superficie de todas las que he desarrollado hasta el momento. Son 10 kilómetros en vertical, de Norte a Sur, por 20 kilómetros en horizontal, de Este a Oeste. En ella aparece representada gran parte de la Sierra de Guadarrama y en el mapa he marcado la ruta de La Cuerda Larga, ruta que, de cumbre en cumbre, recorre un trayecto directo entre el Puerto de Navacerrada y el Puerto de La Morcuera por encime de lo que los madrileños conocen como "La sierra de Madrid". Al igual que en Peñalara 3D Map en ésta aplicación he marcado en verde más intenso otro parque natural, el Parque Regional de La Pedriza del Manzanares, y también aparece marcado en azul la Estación de Esquí de Valdesquí, esta vez en su totalidad. Quien haya probado Peñalara 3D Map dirá que la textura de ésta última aplicación es mejorable pero yo le diría que el "peso" total de la aplicación y la cantidad de recursos consumidos en tiempo de ejecución hacen inviable una mejora. 


En esas imágenes vemos el comienzo de la ruta desde el Puerto de Navacerrada (1) y distintas partes de la ruta de La Cuerda Larga (2, 3 y 4), La Pedriza desde distintos puntos de la ruta (5, 6 y 8) y una imagen del Pico de La Maliciosa (8).

Voy a acabar la entrada de hoy con un resumen de mis siguientes pasos con respecto a "mis montañas 3D". 

1.- Mejorar las texturas de Gredos Virtual 3D y Ordesa Virtual 3D
2.- Mostrar información de la posición en modo juego
3.- Habilitar "modo niebla" (cuando estás dentro del mapa)
4.- Mejorar la pantalla de inicio de cada aplicación, todas son iguales con alguna variación.

PD: ¿Quieres o tienes especial interés en tener disponible alguna zona concreta en 3D con éste formato? Pídemelo sin problema, en mi lista de mapas pendientes tengo varios valles del Pirineo y de Picos de Europa y aunque no tengo pensado seguir haciendo mapas hasta que no acabe las mejoras en "el motor 3D" si quieres que saque alguno con el estado actual (modo juego sin datos de posición) sólo tienes que pedírmelo y lo haré encantado.

Un saludo.

Juan García Carmona

12 sept. 2012

Primeros pasos con Moq: un ejemplo básico de TDD con Mocking

Después de "Introducción a los Tests Unitarios, TDD y Mocking" y tras la serie de artículos sobre GRASP hoy me he decidido a mostrar de forma práctica el TDD con Mocking. Quiero hacerlo así, con uno o dos ejemplos sencillos porque estoy preparando unas sesiones de TDD para patrones de diseño, es decir, como enfocar un desarrollo guiado por pruebas cuando se va a atacar cierto patrón de diseño. 

En el ejemplo de hoy voy a partir de aquel ejemplo de la calculadora pero hoy voy a empezar desde cero.  Voy a hacer un diagrama estático de clases primero, para definir una serie de paquetes, interfaces y clases, y un diagrama de secuencia después, para ver cuál va a ser el caso de uso que voy a atacar mediante TDD y Mocking. 

Voy a improvisar unos requisitos: "Queremos desarrollar una calculadora que delegue sus cálculos en una serie de servicios de cálculo. Para comenzar queremos que nuestra calculadora sea capaz de sumar y restar dos números enteros positivos utilizando para tal fin los servicios SumCalculatorService y RestCalculatorService a cuyas interfaces tenemos acceso."

Bueno, voy a hacer el diagrama estático de clases de todo ésto, a ver qué sale:

¿Se ve claro? Seguro que si pero lo explico, hay dos paquetes, uno el cliente, en azul y otro el servidor, en rojo. El servidor contiene o contendrá los servicios de suma (ISumCalculatorService) y de resta (IRestCalculartorService). Está la clase Calculadora que tiene dependencias (linea punteada con flecha simple hacia la dependencia) con ambos servicios y que implementa (linea punteada con felcha triangular blanca) la interfaz ICalculator. En cuanto al servidor he dicho que contiene o contendrá los servicios y digo contiene o contendrá porque haciendo TDD con Mocking no nos hace falta tener el sistema completo ya que lo que voy a hacer va a ser simular el comportamiento de las dependencias de la clase o de la funcionalidad concreta de la clase que se quiere probar.
A veces siento que soy muy enrevesado expresándome pero es lo que hay, no hay una manera menos enrevesada de explicarlo. El desenrevesador que lo desenrevese buen desenrevesador será. XD!
Antes de empezar a tirar lineas de código de pruebas  voy  a definir un primer ámbito en las pruebas. Las primeras pruebas pueden ser del tipo WhenCreatingTheObject, es decir, cuando se crea el objeto. Aquí  voy  a probar la inyección de dependencias en la clase calculadora y que de verdad implemente ICalculator. 

Y ya va siendo hora de abrir el Visual Studio para crear el proyecto del cliente, al que voy a llamar PrimerosPasosConMoq, y el proyecto que alojará los tests, PromerosPasosConMoqTests. Por cierto, dentro de dos días sale Visual Studio 2012 y creo que me voy a comprar la versión profesional y a estudiarme las mejoras a fondo, cuando lo hice con el 2010 fue duro al principio pero ahora no puedo vivir sin él y me pasó exactamente lo mismo con el Visual Studio 2005. 
Una vez creados los proyectos  voy  con las interfaces, tendré por un lado las de los servicios, que yo las escribo aquí pero que podrían, o deberían mejor dicho, estar en un ensamblado aparte:
public interface ISumCalculatorService
{
    int SumaDosNumeros(int num1, int num2);
}
public interface IRestCalculatorService
{
    int RestaDosNumeros(int num1, int num2);
}
Y ya si en el proyecto "definitivo" la interfaz ICalculator con los métodos que se han definido en el diagrama de clases:
public interface ICalculator
{
    int SumaDosNumeros(int num1, int num2);
    int RestaDosNumeros(int num1, int num2);
}
Ahora  voy  al proyecto de tests y le agrego las siguientes referencias, una al proyecto "definitivo" y otra a la librería de Moq (se supone que cualquier desarrollador con un mínimo de experiencia es capaz de encontrar Moq, descargarlo, descomprimirlo y añadir la referencia a la librería correspondiente a su proyecto de tests), la tercera referencia es a la librería de .Net Microsoft.VisualStudio.QualityTools.UnitTestFramework y la última al proyecto con la clase UnitTestBase que contiene y obliga a utilizar el patrón AAA en nuestros tests.


Bien, pues ya va siendo hora de empezar a pensar en tests. Como decía, lo primero que hay que probar es la construcción del objeto calculadora, la inyección de dependencias, y para ello voy a crear una clase WhenCreatingCalculadora que, para cumplir con la metodología AAA heredará de UnitTestBase. Como recordatorio voy a pegar el código de UnitTestBase pero ya estaba disponible en "Introducción a los Tests Unitarios, TDD y Mocking":

using Microsoft.VisualStudio.TestTools.UnitTesting;
/* *
 * 
 * Los tests unitarios tienen un comportamiento muy simple:
 * 
 * 1º Arrange()    -> Organizar las precondiciones
 * 2º Act()        -> Actuar, es decir, ejecutar lo que se quiere probar
 * 3º Assert()     -> Verificar que se han cumplido las postcondiciones
 * 
 * Esta visión simplista junto con una buena nomenclatura harán tu TDD mucho más sencillo.
 * 
 * Juan García Carmona
 * 
 * */

namespace ArrangeActAssert
{
    [TestClass]
    public abstract class UnitTestBase
    {
        [TestInitialize]
        public void Init()
        {
            Arrange();
            Act();
        }

        protected virtual void Arrange()
        {
        }

        protected abstract void Act();

        [TestCleanup]
        public void Cleanup()
        {
            System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown();
        }
    }
}
Según escribo el código de WhenCreatingCalculadora veo un error, no existe la clase Calculadora, es buena señal pues estamos haciendo TDD y se trata de escribir pruebas y refactorizar el código para que las pruebas compilen y pasen. Voy a crear una clase Calculadora vacía para que compile y, de momento, el código de WhenCreatingCalcualdora tiene el siguiente aspecto:
using ArrangeActAssert;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using PrimerosPasosConMoq.Ejemplo1.Client;
using PrimerosPasosConMoq.Ejemplo1.Server;

namespace PrimerosPasosConMoqTests.Ejemplo1.Client.CalculadoraTests
{
    [TestClass]
    public class WhenCreatingCalculadora : UnitTestBase
    {
        protected Calculadora _objectoToTest;
        protected Mock<IRestCalculatorService> RestCalculatorServiceMock;
        protected Mock<ISumCalculatorService> SumCalculatorServiceMock;

        protected override void Arrange()
        {
            base.Arrange();
            RestCalculatorServiceMock = new Mock();
            SumCalculatorServiceMock = new Mock();
        }

        // Act, como queremos que lance excepciones, lo voy a trasladar a cada método
        protected override void Act(){}

    }
}
En ése código destacan los objetos Mock, son objetos falsos que implementan cada una de las interfaces, IRestCalculatorService e ISumCalculatorService, y son los que voy a utilizar para escribir las pruebas sobre la inyección de dependencias. La primera prueba puede ser que si al primer parámetro le pasamos un objeto nulo el constructor lance una excepción. El código del test quedará así:
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ItShouldThrowAnArgumentNullExceptionWhenSumCalculatorServiceIsNull()
{
    _objectoToTest = new Calculadora(null, RestCalculatorServiceMock.Object);
}
Pero claro, éste código da errores de compilación porque Calculadora, que es una clase vacía, no tiene ningún constructor que acepte dos argumentos. ¿Por qué no lo tiene? Porque aún no lo he escrito. De nuevo hay que refactorizar para adaptar el código a los tests, voy a hacer que Calculadora herede de ICalculator y a ponerle un constructor que acepte dos argumentos.
public class Calculadora : ICalculator
{
    public Calculadora (ISumCalculatorService sumService, IRestCalculatorService restService)
    {
    }

    public int SumaDosNumeros(int num1, int num2)
    {
        throw new NotImplementedException();
    }

    public int RestaDosNumeros(int num1, int num2)
    {
        throw new NotImplementedException();
    }
}
Bien, ahora que el código compila es el momento de ejecutar el primer test. Lo ejecuto y, tal y como esperaba, el test falla porque el constructor no lanza ninguna excepción si el primer parámetro es nulo. el mensaje es evidente:
WhenCreatingCalculadora. ItShouldThrowAnArgumentNullExceptionWhenSumCalculatorServiceIsNull did not throw expected exception System.ArgumentNullException.
-- Exception doesn't have a stack trace --

Pues es el momento, de nuevo, de refactorizar. Ahora que "tenemos la luz roja" es hora de buscar el verde, es decir, de que pase éste test, para poder continuar. Me voy a la clase calculadora y modifico el constructor dejándolo así:
public Calculadora (ISumCalculatorService sumService, IRestCalculatorService restService)
{
    if (sumService == null)
        throw new ArgumentNullException("sumService");
}
Vuelvo a ejecutar el test y ahora pasa:


Ahora voy a probar con el segundo parámetro, escribimos el test:
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ItShouldThrowAnArgumentNullExceptionWhenRestCalculatorServiceIsNull()
{
    _objectoToTest = new Calculadora(SumCalculatorServiceMock.Object,null);
}
Lo ejecuto y, de nuevo tal y como era de esperar, el test falla:


De nuevo modifico el constructor para que pasen las pruebas y me queda de la siguiente forma:
public Calculadora (ISumCalculatorService sumService, IRestCalculatorService restService)
{
    if (sumService == null)
        throw new ArgumentNullException("sumService");
    if (restService == null)
        throw new ArgumentNullException("restService");
}
Y en verdad, ejecuto las pruebas y, ahora si, tengo luz verde para continuar:


En definitiva ésto es TDD, espero que este ejemplo lo deje claro. Pero voy a seguir, voy a explotarlo un poco más. Antes me ha dado por hacer que Calculadora implementara ICalculator pero podría no haberlo hecho y tendría, para cumplir con el diseño, asegurarme de que el objeto que estoy probando es una instancia de ICalculator y eso se podría asegurar con el siguiente test (que quizá debería haber sido el primero):
[TestMethod]
public void ItShouldImplementICalculator()
{
    _objectoToTest = new Calculadora(SumCalculatorServiceMock.Object, RestCalculatorServiceMock.Object);
    Assert.IsNotNull(_objectoToTest as ICalculator);
}
Vuelvo a ejecutar la batería de tests y pasan los tres tests que llevo escritos hasta el momento, tests que nos aseguran que la clase Calculadora implementa ICalculator y que está protegida frente a nulos en la inyección de dependencias. Éste último tests, el que verifica que se implemente ICalculator, podemos ampliarlo un poco más ya que al ejecutar el constructor con los parámetros correctos nos devuelve el objeto que esperamos. Podemos modificar el nombre y las verificaciones así:
[TestMethod]
public void ItShouldReturnACalculatorThatImplementICalculatorWhenParamsAreOk()
{
    _objectoToTest = new Calculadora(SumCalculatorServiceMock.Object, RestCalculatorServiceMock.Object);
    // Es una calculadora, se ha creado correctamente
    Assert.IsNotNull(_objectoToTest);
    // y además implementa ICalculator
    Assert.IsNotNull(_objectoToTest as ICalculator);
}
No solamente pasan los tests...


...sino que además los tests sirven de documentación sobre el código bajo pruebas, ¿no es genial? Dejo todo el código de la clase de pruebas con estos tres tests sobre el constructor:
using System;
using ArrangeActAssert;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using PrimerosPasosConMoq.Ejemplo1.Client;
using PrimerosPasosConMoq.Ejemplo1.Server;

namespace PrimerosPasosConMoqTests.Ejemplo1.Client.CalculadoraTests
{
    [TestClass]
    public class WhenCreatingCalculadora : UnitTestBase
    {
        protected Calculadora _objectoToTest;
        protected Moc<IRestCalculatorService> RestCalculatorServiceMock;
        protected Mock<ISumCalculatorService> SumCalculatorServiceMock;

        protected override void Arrange()
        {
            base.Arrange();
            RestCalculatorServiceMock = new Mock<IRestCalculatorService>();
            SumCalculatorServiceMock = new Mock<ISumCalculatorService>();
        }


        // Act, como queremos que lance excepciones, lo voy a trasladar a cada método
        protected override void Act() { }

        [TestMethod]
        public void ItShouldReturnACalculatorThatImplementICalculatorWhenParamsAreOk()
        {
            _objectoToTest = new Calculadora(SumCalculatorServiceMock.Object, RestCalculatorServiceMock.Object);
            // Es una calculadora, se ha creado correctamente
            Assert.IsNotNull(_objectoToTest);
            // y además implementa ICalculator
            Assert.IsNotNull(_objectoToTest as ICalculator);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ItShouldThrowAnArgumentNullExceptionWhenSumCalculatorServiceIsNull()
        {
            _objectoToTest = new Calculadora(null, RestCalculatorServiceMock.Object);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ItShouldThrowAnArgumentNullExceptionWhenRestCalculatorServiceIsNull()
        {
            _objectoToTest = new Calculadora(SumCalculatorServiceMock.Object, null);
        }

    }
}
Voy a seguir haciendo TDD pero ahora sobre el uso de la calculadora, porque hasta ahora los objetos Mock que he utilizado sólo los he utilizado de adorno. Querido lector, es ahora donde empieza la auténtica magia, vamos a aplicar la herencia y la nomenclatura del primer ejemplo de tests unitarios en la calculadora y vamos a crear las siguientes clases:

WhenUsingTheCalculator : UnitTestBase
[TestClass]
public abstract class WhenUsingTheCalculator : UnitTestBase
{
    protected Calculadora _objectToTest;
    protected Mock RestCalculatorServiceMock;
    protected Mock SumCalculatorServiceMock;

    protected override void Arrange()
    {
        base.Arrange();
        RestCalculatorServiceMock = new Mock();
        SumCalculatorServiceMock = new Mock();
        // Esto ya lo hemos probado y sabemos que funciona:
        _objectToTest = new Calculadora(SumCalculatorServiceMock.Object, RestCalculatorServiceMock.Object);
    }
}
WhenAddingTwoNumbers : WhenUsingTheCalculator
[TestClass]
public abstract class WhenAddingTwoNumbers : WhenUsingTheCalculator
{
    protected abstract int X { get; }
    protected abstract int Y { get; }
    protected int result;

    protected override void Arrange()
    {
        base.Arrange();
        // Cuando al servicio 'falso' le pase dos números
        // Que devuelva la suma de ambos
        SumCalculatorServiceMock.Setup(x => x.SumaDosNumeros(X, Y)).Returns(X + Y);
    }

    protected override void Act()
    {
        result = _objectToTest.SumaDosNumeros(X, Y);
    }
}
Aquí vemos la primera simulación con objetos Mock, le estamos diciendo a SumCalculatorServiceMock que cuando la clase que estamos probando llame al método SumaDosNumeros del objeto falso, éste debe devolver la suma de dichos números.
Veamos que sucede cuando sumemos dos números posirtivos:
WhenAddingTwoPositiveNumbers : WhenAddingTwoNumbers
[TestClass]
public class WhenAddingTwoPositiveNumbers : WhenAddingTwoNumbers
{
    protected override int X { get { return 13; } }
    protected override int Y { get { return 45; } }

    [TestMethod]
    public void ItShouldReturnTheCorrectResult()
    {
        Assert.AreEqual(58, result);
    }
}
Si ejecutamos éste tests deberíamos llegar al método Calculadora.SumaDosNumeros(x,y) y, tal y como lo dejamos antes éste lanzará una NotImplementedException y por tanto el tests fallará:


Awesome! Isn`t it? A mi particularmente me encanta... Tengo luz roja y es algo esperado, ahora lo que toca es lo de siempre, refactorizar hasta que pase el test. Ésto que implica, pues necesito una variable interna en la clase para que al construirla utilice la instancia del servicio que se le ha pasado al constructor (la primera dependencia inyectada).La clase Calculadora quedaría así:

public class Calculadora : ICalculator
{
    private readonly ISumCalculatorService _sumService;
    
    public Calculadora (ISumCalculatorService sumService, IRestCalculatorService restService)
    {
        if (sumService == null)
            throw new ArgumentNullException("sumService");
        if (restService == null)
            throw new ArgumentNullException("restService");

        _sumService = sumService;
    }

    public int SumaDosNumeros(int num1, int num2)
    {
        return _sumService.SumaDosNumeros(num1, num2);
    }

    public int RestaDosNumeros(int num1, int num2)
    {
        throw new NotImplementedException();
    }
}
¿Pasará el test?

Hagamos lo mismo pero para restar, crearé una clase base para la resta y una clase concreta con ciertos valores para restar en la que estableceré el comportamiento esperado de nuestro servicio falseado, en éste caso el de la resta:
[TestClass]
public abstract class WhensubtractingTwoNumbers : WhenUsingTheCalculator
{
    protected abstract int X { get; }
    protected abstract int Y { get; }
    protected int result;

    protected override void Arrange()
    {
        base.Arrange();
        // Cuando al servicio 'falso' le pase dos números
        // Que devuelva la resta de ambos
        RestCalculatorServiceMock.Setup(x => x.RestaDosNumeros(X, Y)).Returns(X - Y);
    }

    protected override void Act()
    {
        result = _objectToTest.RestaDosNumeros(X, Y);
    }
}
[TestClass]
public class WhenSubtractingTwoPositiveNumbers : WhensubtractingTwoNumbers
{
    protected override int X { get { return 13; } }
    protected override int Y { get { return 45; } }

    [TestMethod]
    public void ItShouldReturnTheCorrectResult()
    {
        Assert.AreEqual(-32, result);
    }
}
Y, bueno, evidentemente, igual que con SumaDosNumeros, como el método no está implementado, dará una NotImplementedException, tras lo cuál voy a implementar el método así:
public class Calculadora : ICalculator
{
    private readonly ISumCalculatorService _sumService;
    private readonly IRestCalculatorService _subsService;
    
    public Calculadora (ISumCalculatorService sumService, IRestCalculatorService restService)
    {
        if (sumService == null)
            throw new ArgumentNullException("sumService");
        if (restService == null)
            throw new ArgumentNullException("restService");

        _sumService = sumService;
        _subsService = restService;
    }

    public int SumaDosNumeros(int num1, int num2)
    {
        return _sumService.SumaDosNumeros(num1, num2);
    }

    public int RestaDosNumeros(int num1, int num2)
    {
        return _subsService.RestaDosNumeros(num1, num2);
    }
}
De nuevo todos los tests pasan:


Y de nuevo se ve vemos la nomenclatura de los tests nos ayuda a ver qué hace y cómo se comporta nuestra clase Calculadora.

Espero que éste tutorial de TDD utilizando Mock sirva a muchos desarrolladores para adentrarse en el mundo del Mocking. No he entrado en mucho detalle sobre Moq en realidad, quizá deba dedicarle tiempo a otro artículo a hacer ejemplos un poco más avanzados sobre Moq y ver de forma práctica qué cosas se pueden hacer con Moq. No obstante voy a dejar un enlace a una guía rápida que hay de Moq (Introduction to Moq) para que todo aquel que quiera continuar con éste ejemplo lo haga por su cuenta pues, voy a detener la sesión de TDD por hoy. Evidentemente no he terminado pues habría que testar (WhenUsingCalculator) malos usos o excepciones como, igual que en el primer ejemplo, cuando superamos los rangos en sumas y restas y ese tipo de cosas. 

Tanto si te ha sido de ayuda éste u otro tutorial o artículo o tienes alguna duda, queja, sugerencia, petición o crítica tus comentarios son bienvenidos y siempre puedes ponerte en contacto directamente conmigo vía g+ o por correo electrónico. 

Juan García Carmona

7 sept. 2012

Patrones generales de asignación de responsabilidades

En el mundo anglosajón gustan mucho los juegos de palabras. Esto lo digo porque no creo que sea casualidad que se haya elegido el acrónimo GRASP para designar a los "General Responsibility Assignment Software Patterns", o lo que es lo mismo, patrones generales de asignación de responsabilidades en software ya que 'grasp' significa comprender, entender, alcanzar, etcétera. De igual forma estoy seguro de que SOLID, que significa sólido, tampoco fue elegido al azar. 

Con éste breve artículo quiero cerrar la serie de artículos sobre GRASP que he ido subiendo a lo largo de la mañana de hoy en los que he ido mostrando una serie de buenas prácticas enfocadas a la calidad del software, calidad "estructural" por llamarla de alguna manera, ya que no se ha centrado en pruebas sino en la estructura y las responsabilidades de las clases que componen el software que desarrollamos. 

Como acabo de decir, más que patrones GRASP hay que considerarlo como una serie de buenas prácticas, unos pocos consejos que hay que seguir si se quiere hacerlo bien. Tanto si eres estudiante de informática y acabas de empezar a programar como si llevas unos cuantos años en el mundillo, estos siete consejos esta bien que los repases y los tengas presentes de hoy en adelante cada vez que quieras desarrollar software con un mínimo de calidad. Aquí y ahora sólo voy a enumerarlos enlazando a cada uno de los artículos que ya hay subidos. En cada artículo encontrarás una descripción y varios ejemplos prácticos de código generalmente autoexplicativos pero que suelen traer alguna aclaración.
No te olvides tampoco de los principios SOLID para, siguiendo con el juego de palabras en inglés, conseguir una comprensión sólida de lo que estás haciendo. 

Juan García Carmona

GRASP: Variaciones protegidas

Éste es el último de los artículos sobre los principios GRASP. Se han escrito libros enteros sobre éste tema pero he intentado resumir cada principio al máximo dando ejemplos prácticos para que cada cuál saque sus propias conclusiones. En ésta ocasión voy a hablar sobre el principio de las variaciones protegidas, el último principio si los ordenamos alfabéticamente pero no por ello el menos importante.

Siempre se ha dicho que, aplicando metodologías ágiles al desarrollo de software, el cambio es bienvenido, ahora bien, el cambio es bienvenido si lo esperamos. ¿Cuántos proyectos de software se habrán ido a pique y cuantos profesionales habrán visto su reputación por los suelos cuando el cliente ha pedido un pequeño cambio cuyas implicaciones obligaron a arquitectos y desarrolladores a empezar de cero? No sé la respuesta pero seguro que han sido demasiados.

El cambio debe ser bienvenido, claro, porque los clientes siempre quieren cambios y les cobraremos por desarrollarlos, pero no deben ser motivo de desesperación. Variaciones protegidas, es el principio fundamental de protegerse frente al cambio. Esto quiere decir que lo que veamos en un análisis previo que es susceptible de modificaciones lo envolvamos en una interfaz y utilicemos el polimorfismo para crear varias implementaciones y posibilitar implementaciones futuras de manera que quede lo menos ligado posible a nuestro sistema. De ésta forma, cuando se produzca la variación o el cambio que esperamos, dicho cambio nos repercuta lo mínimo.Este principio está muy relacionado con el polimorfismo y la indirección.

Imaginemos el siguiente código:
public class ImagenJpeg
{
    public void Redimensionar(int nuevoAlto, int nuevoAncho)
    {
        // Resize
    }
}

public class MostrarImagenController
{
    public void BotonRedimensionarClicked(ImagenJpeg image)
    {
        image.Redimensionar(10, 20);
    }
}
¿Qué problemas pueden surgir? Simple, puede suceder que en vez de una imagen jpeg nos manden otro tipo de imagen. ¿Y cómo lo solucionamos? También simple si has seguido toda la serie de artículos sobre GRASP y las distintas refactorizaciones que hemos hecho en cada caso, basta con añadir una interfaz IImagen con un método Redimensionar, dejando el código así:
public interface IImagen
{
     void Redimensionar(int nuevoAlto, int nuevoAncho);
}

public class ImagenJpeg : IImagen
{
    public void Redimensionar(int nuevoAlto, int nuevoAncho)
    {
        // Redimensiona una imagen...
    }
}

public class MostrarImagenController
{
    public void BotonRedimensionarClicked(IImagen image)
    {
        image.Redimensionar(10, 20);
    }
}
¡Genial! Ya no nos tendría que importar que cambiara el tipo de imagen a redimensionar, al menos a éste nivel en nuestra aplicación, y éste ejemplo es extrapolable a casi cualquier situación.

Bueno, aquí acaba la serie de artículos sobre patrones y principios GRASP en los que hemos ido viendo las mejores formas de asignar responsabilidades a clases y métodos para ganar en cohesión, disminuir acoplamiento, aplicar mejor los principios SOLID, protegernos frente al cambio y, en definitiva, ser mejores desarrolladores. Espero que ésta serie de artículos sea útil y recibir comentarios críticas y peticiones de los lectores.

Juan García Carmona

GRASP: Polimorfismo

A lo largo de todos los artículos que llevo escritos en éste blog ha aparecido la palabra polimorfismo más de una decena de veces, seguro, y he dado por hecho que todo el mundo sabía de qué estaba hablando, pero ¿Qué es polimorfismo?

Polimorfismo, en programación orientada a objetos, es un concepto muy simple con el que la gente a veces se lía, polimorfismo es permitir que varias clases se comporten de manera distinta dependiendo del tipo que sean. 

Siempre que se tenga que llevar a cabo una responsabilidad que dependa de un tipo, se tiene que
hacer uso del polimorfismo, es decir, asignaremos el mismo nombre a servicios implementados en 
diferentes objetos acordes con cada tipo.

Como de costumbre, veamos ésto con un ejemplo "malo", es decir, mejorable, la explicación y un ejemplo que lo soluciona.

Mal:
public enum TipoDeLog
{
    Debug,
    Error
}

public class Log
{
    StreamWriter _ficheroLog;

    public void Registrar(string mensaje, TipoDeLog tipoDeLog)
    {
        switch (tipoDeLog)
        {
            case TipoDeLog.Debug:
                _ficheroLog.WriteLine("[DEBUG];{0}", mensaje);
                break;
            case TipoDeLog.Error:
                _ficheroLog.WriteLine("[ERROR]:{0}", mensaje);
                break;
        }
    }
}

public class CualquierPresentador
{
    Log _log;
    public void AlgoHaSucedido()
    {
        _log.Registrar("Algo ha sucedido y queremos dejar constancia en un registro.", TipoDeLog.Debug);
    }
}
¿Por qué?

Se ve claramente la dependencia de la clase Log y el método registrar con el TipoDeLog. Podemos evitar ésto creando una interfaz para el mensaje implementada de dos formas distintas, una por cada tipo, y sólo tendremos que pedir a la clase Log que registre un IMensaje. Y dependiendo de qué mensaje queramos registrar, quien sepa de qué tipo debe ser registrado, es decir, el experto en información, que cree una instancia concreta y llame a Log.Registrar.

Bien:
public interface IMensajeDelLog
{
    string Valor { get; }
}

public class MensajeDebug : IMensajeDelLog
{
    readonly string _mensaje;

    public MensajeDebug(string mensaje)
    {
        _mensaje = mensaje;
    }

    public string Valor
    {
        get
        {
            return string.Format("[DEBUG];{0}", _mensaje);
        }
    }
}

public class MensajeError : IMensajeDelLog
{
    readonly string _mensaje;

    public MensajeError(string mensaje)
    {
        _mensaje = mensaje;
    }

    public string Valor
    {
        get
        {
            return string.Format("[ERROR];{0}", _mensaje);
        }
    }
}

public class Log
{
    StreamWriter _ficheroLog;

    public void Registrar(IMensajeDelLog message)
    {
        _ficheroLog.WriteLine(message.Valor);
    }
}

public class CualquierPresentador
{
    Log _log;

    public void AlgoHaSucedidoYQueremosRegistrarUnError()
    {
        _log.Registrar(new MensajeError("Algo ha sucedido y queremos dejar constancia en un registro."));
    }

    public void AlgoHaSucedidoYQueremosRegistrarloParaDepurar()
    {// Por ejemplo una excepción
        _log.Registrar(new MensajeDebug("Algo ha sucedido y queremos dejar constancia en un registro."));
    }
}

Como vemos, aplicar polimorfismo nos lleva a aplicar otros principios y mejora la calidad de nuestro código. Que nunca falte una buena cantidad de polimorfismo en vuestros programas.

Juan García Carmona

GRASP: Indirección

Voy llegando al final de los patrones o principios GRASP, éste, la indirección, es fundamental y, como buenos profesionales, deberíamos tenerlo muy presente en nuestros análisis y decisiones arquitecturales.

El patrón de indirección nos permite mejorar el bajo acoplamiento entre dos clases asignando la responsabilidad de la mediación entre ellos a una clase intermedia.

Problema: ¿dónde debo asignar responsabilidades para evitar o reducir el acoplamiento directo entre elementos y mejorar la reutilización? Es decir, si sabemos que un objeto utiliza otro que muy posiblemente va a cambiar, ¿cómo protegemos a un pobjeto frente a cambios previsibles?

Solución: asignar la responsabilidad a un objeto que medie entre los elementos para proteger al primer objeto de los posibles cambios del segundo.

public class CualquierPresentador
{
    Log4Net _logger;

    public void AlgoHaSucedido()
    {
        _logger.Log("Algo ha sucedido y hemos tratado en el presentador dicho algo...");
    }
}

public class Log4Net
{
    public void Log(string message)
    {
    }
} 
En éste caso Log4Net podría cambiar, de hecho esperamos que cambie porque lo estamos utilizando, si, pero se nos queda cortos. ¿Cómo aplicamos la indirección?

¿Qué tal si creamos un servicio de loggin intermedio? Así el presentador utilizará el servicio y con un poco de suerte los cambios en Log4Net no afectarán demasiado al resto del programa.

public class CualquierPresentador
{
    private ServicioLog _logger;

    public void AlgoHaSucedido()
    {
        _logger.Log("Algo ha sucedido y hemos tratado en el presentador dicho algo...");
    }
}

public class ServicioLog
{
    private Log4Net _logger;

    public void Log(string message)
    {
        _logger.Log(message);
    }
}

public class Log4Net
{
    public void Log(string message)
    {
    }
}

 Este patrón es fundamental para crear abstracciones ya que nos permite introducir API's externas en la aplicación sin que tengan demasiada influencia en el código que tendría que cambiar cuando cambiara la API. Además, la primera clase podría cambiarse fácilmente para que en vez de ésta API utilizara cualquier otra.

Juan García Carmona

GRASP: Fabricación pura

Siguiendo con la serie de artículos sobre los patrones o principios GRASP ahora toca hablar de la fabricación pura.

La fabricación pura se da en las clases que no representan un ente u objeto real del dominio del problema sino que se han creado intencionadamente para disminuir el acoplamiento, aumentar la cohesión y/o potenciar la reutilización del código. 

La fabricación pura es la solución que surge cuando el diseñador se encuentra con una clase poco cohesiva y que no tenie otra clase en la que implementar algunos métodos. Es decir que se crea una clase "inventada" o que no existe en el problema como tal, pero que, añadiéndola, logra mejorar estructuralmente el sistema. Como contraindicación deberemos mencionar que al abusar de este patrón suelen aparecer clases función o algoritmo, esto es, clases que tienen un solo método.

Un ejemplo de porqué tendríamos que inventarnos una clase fuera del dominio del problema, imaginemos que estamos programando el famoso Angry Birds:
public class PajaroEnfadado
{
    public void Volar()
    {
    }

    public void Mostrar()
    {
        // En una interfaz de usuario
    }
}
El método Mostrar "acopla" al pájaro a la interfaz de usuario y como no queremos enfadarlo aún más haríamos lo siguiente:
public class PajaroEnfadado
{
    public void Volar()
    {
    }
}

public class PajaroEnfadadoPresenter
{
    public PajaroEnfadadoPresenter(PajaroEnfadadoView view)
    {
        view.Mostrar(new PajaroEnfadado());
    }
}

public class PajaroEnfadadoView
{
    public void Mostrar(PajaroEnfadado pajaroEnfadado)
    {
    }
}

Ahora tenemos dos nuevas clases, view y presenter. Estas dos clases no existen en el dominio real del problema pero desacoplan enormemente la implementación del pájaro de la interfaz de usuario... Y nos dejan que la implementación del pájaro enfadado se centre en la lógica de negocio y en nada más, una única responsabilidad...


¿No te suena? Seguro que si pero quizá nunca te habías preguntado el por qué de las arquitecturas de n capas, me da igual que sea MVC que MVP, que MVVM o MVVMP. Es una idea simple e intuitiva a la que ahora puedes ponerle un nombre y decir que se basa en el patrón GRASP de "Fabricación pura".

Juan García Carmona

GRASP: Experto en información

Éste es el cuarto artículo de la serie de artículos de principios o patrones GRASP. En ésta ocasión vamos a hablar del "Experto en información". Éste es el principio básico de asignación de responsabilidades,  la S de SOLID, y para mí es de los más importantes de los patrones o principios GRASP.

Experto en información nos dice que la responsabilidad de la creación de un objeto o la implementación de un método, debe recaer sobre la clase que conoce toda la información necesaria para crearlo. De este modo obtendremos un diseño con mayor cohesión y así la información se mantiene encapsulada, es decir, disminuye el acoplamiento.

Como con otras veces, voy a poner un ejemplo que viola éste principio, la explicación de por qué lo viola y un ejemplo que lo soluciona.

Ejemplo que incumple "Experto en información":
public class Informe
{
    public int[] Parciales { get; set; }
}

public class InformePresenter
{
    Informe _informe;

    public void CalculateTotalButtonClicked()
    {
        var total = 0;
        foreach (var parcial in _informe.Parciales)
        {
            total = total + parcial;
        }

        // TRUCO GRATIS: éste bucle lo podemos convertir en un una expresión funcional usando Linq:
        // var total = _informe.Parciales.Aggregate(0, (current, parcial) => current + parcial);
    }
}
¿Por qué viola éste principio? 

Por lo mismo que viola la S de SOLID, la responsabilidad de InformePresenter es, como su propio nombre indica, presentar un informe y en éste caso está calculando el total. ¡Mal! El total debería dárselo la clase Informe, que es la que tiene toda la información al respecto, ella es la experta y quien debería calcularlo.

Solución:
public class Informe
{
    public int[] Parciales { get; set; }

    public void CalcularTotal()
    {
        var total = 0;
        foreach (var parcial in Parciales)
        {
            total = total + parcial;
        }
    }
}

 public class InformePresenter
{
    private Informe _informe;

    public void CalculateTotalButtonClicked()
    {
       _informe.CalcularTotal();
    }
}
Regla de oro: 

Éste es el principio que se debería aplicar más a menudo, el que hay que tener más en cuenta. La suma de "Experto en información" y SRP es que una clase sólo debería tener un mótivo para cambiar y debería ser ella misma la encargada de crear los objetos e implementar los métodos sobre los que es experta.

Juan García Carmona

GRASP: Creador

Éste es el tercer artículo sobre GRASP, en él vemos el patrón creador, otro principio fácil de aplicar  en nuestros desarrollos que está intimamente relacionado con los principios SOLID. 

La creación de instancias es una de las actividades más comunes en un sistema orientado a objetos. En consecuencia es útil contar con un principio general para la asignación de las responsabilidades de creación. Si se asignan bien el diseño puede soportar un bajo acoplamiento, mayor claridad, encapsulamiento y reutilización.

El patrón creador nos ayuda a identificar quién debe ser el responsable de la creación o instanciación de nuevos objetos o clases. Éste patrón nos dice que la nueva instancia podrá ser creada por una clase si:
  • Contiene o agrega la clase. 
  • Almacena la instancia en algún sitio (por ejemplo una base de datos)
  • Tiene la información necesaria para realizar la creación del objeto (es 'Experta')
  • Usa directamente las instancias creadas del objeto
Una de las consecuencias de usar este patrón es la visibilidad entre la clase creada y la clase creadora. Una ventaja es el bajo acoplamiento, lo cual supone facilidad de mantenimiento y reutilización.

Veamos, con una serie de ejemplos de cómo aplicar éste patrón correctamente bajo distintas circunstancias. 

Ejemplo 1:
public class ListaDeClientesPresenter
{
    Cliente _clientes;

    public void AddButtonClicked()
    {
    }
}

public class Cliente
{
    List _pedidos;
}

public sealed class Pedido
{
}
¿Qué pasa aquí?
Donde quiera que se llame al método AddButtonClicked se generará un nuevo pedido y se lo "daremos" a una instancia de un cliente, pero ¿quién debería crear ese pedido? Lo que nos dice éste principio es que esa responsabilidad debería recaer en Cliente ya que contiene Pedidos.

Ejemplo 2:
public class ListaDeClientesPresenter
{
    Cliente _cliente;

    public void AddButtonClicked()
    {
        _cliente.AddPedido();
    }
}

// En éste caso contiene o agrega la clase y además maneja varias instancias...
public class Cliente
{
    List _pedidos;

    public void AddPedido()
    {
        var nuevoPedido = new Pedido();
        _pedidos.Add(nuevoPedido);
    }
}

public sealed class Pedido
{
}
// Pero hay más posibilidades...

Ejemplo 3:
// Si la clase guarda (persiste) la clase creada...
// Lo más fácil es implementar un repositorio:

public class ListaDeClientesPresenter
{
    private RepositorioDePedidos repositorioDePedidos;

    public void AddButtonClicked()
    {
    }
}

public class Cliente
{
    List _pedidos;
}

public class RepositorioDePedidos
{
    Stream _pedidosXml;

    public Pedido LoadOrders()
    {
        // Lo siento, el propósito no es aprender serialización, sólo me interesaba que compilara
        // el código:
        var serialzer = new XmlSerializer(typeof(Pedido));
        var xmlElement = (XmlElement) serialzer.Deserialize(_pedidosXml);
        return new Pedido(xmlElement.Value);
    }
}

public sealed class Pedido
{
    string _id;

    public Pedido(string id)
    {
       _id = id;
    }
}

Ejemplo 4:
// Si la clase tiene la información necesaria para crear la nueva clase
// Por ejemplo si queremos que Pedido tenga el Id de cliente...
public class ListaDeClientesPresenter
{
    Cliente _cliente;

    public void AddButtonClicked()
    {
        _cliente.AddPedido();
    }
}

public class Cliente
{
    List _pedidos;
    int _id;

    public void AddPedido()
    {
        var nuevoPedido = new Pedido(_id);
        _pedidos.Add(nuevoPedido);
    }
}

public sealed class Pedido
{
    private int _clienteId;
    public Pedido(int clienteId)
    {
        _clienteId = clienteId;
    }
}

Ejemplo 5:
// Si la clase usa directamente la clase creada
// Por ejemplo si queremos que el Cliente gestione Pedido/s
public class ListaDeClientesPresenter
{
    Cliente _cliente;

    public void AddButtonClicked()
    {
    }
}

public class Cliente
{
    Pedido _pedido;

    // La responsabilidad de crear esta clase que él mismo maneja tantas veces recaé sobre él
    // Aquí o en cualquier otro lugar...

    public Cliente()
    {
        _pedido = new Pedido();
    }

    public void DeleteOrder()
    {
        _pedido.Borar();
    }

    public void RenameOrder(string newName)
    {
        _pedido.Modificar();
    }
}

public sealed class Pedido
{
    public void Borar(){}
    public void Modificar(){}
}

De nuevo no hay mucho más que decir sobre éste patrón, tan solo que hay que tener en cuenta esos cuatro puntos a la hora de instanciar un objeto y preguntarse si el lugar en el que se está realizando es el idóneo o no. Darse cuenta de que no se está cumpliendo con el patrón creador es un buen motivo para refactorizar nuestro código.

Juan García Carmona

GRASP: Controlador


Éste es el segundo artículo sobre GRASP. Aquí veré brevemente, pues no hay demasiado que decir, el patrón controlador. Creo que cualquier desarrollador del mundo sabe bien lo que es un controlador y el patrón controlador pero voy a poner la definición por si acaso hay algún error de concepto:

El patrón controlador es un patrón que sirve como intermediario entre una determinada interfaz y el algoritmo que la implementa, de tal forma que es el controlador quien recibe los datos del usuario y quien los envía a las distintas clases según el método llamado.

Este patrón sugiere que la lógica de negocio debe estar separada de la capa de presentación, lo que aumenta la reutilización de código y permite a la vez tener un mayor control. Hay muchas arquitecturas de aplicación que se basan en ésto, desde el famoso MVC que ya vi estudiando la carrera hasta la arquitectura MVVM tan utilizada últimamente en aplicaciones de escritorio y la última que he practicado profesionalmente, MVVMP, que no es más que un refinamiento sutil de la anterior.

Se recomienda dividir los eventos del sistema en el mayor número de controladores para poder aumentar así la cohesión y disminuir el acoplamiento.

Un Controlador:
  • Debería ser el primer objeto llamado después de un cambio en la interfaz de usuario.
  • Controla/ejecuta un caso de uso. No hace demasiado por si solo, controla, coordina.
  • Pertence a la capa de aplicación o a la de servicios.
Éste ejemplo se centra en que el controlador debe ser el primer objeto llamado por objetos de la UI:
public class AlbumView
{
}

public class AlbumPresenter
{
    AlbumView _vista;
    AlbumController _controlador;

    public void EtiquetaAdded(string etiqueta)
    {
        _controlador.EtiquetarFoto(etiqueta);
    }
}

public class AlbumController
{
    public void EtiquetarFoto(string newTag)
    {
    }
}
Este otro ejemplo se centra en que el controlador no debería hacer demasiado, tan sólo coordinar:
public class AlbumView
{
}

public class AlbumPresenter
{
    AlbumView _vista;
    AlbumController _controlador;

    public void EtiquetaAdded(string etiqueta)
    {
        _controlador.EtiquetarFoto(etiqueta);
    }
}

public class AlbumController
{
    private RepositorioDeFotos _repository;
    public void EtiquetarFoto(string nuevaEtiqueta)
    {
        var foto = _repository.ReadFoto();
        foto.AddEtiqueta(nuevaEtiqueta);
        _repository.UpdateFoto(foto);
    }
}

public class RepositorioDeFotos
{ // Operaciones CRUD (que significa: Create, Read, Update, Delete)
    public Foto ReadFoto()
    {
        return new Foto();
    }

    public void UpdateFoto(Foto foto)
    {
    }
}

public class Foto
{
    public void AddEtiqueta(string tag)
    {
    }
}
Y no hay mucho más que decir sobre el patrón controlador, su única responsabilidad debe ser controlar, es decir coordinar delegando responsabilidades en otros.
Juan García Carmona

GRASP: Alta cohesión y bajo acoplamiento

Llevo tiempo queriendo escribir una serie de artículos sobre los principios GRASP o principios de asignación de responsabilidades. Éste es el primero de esos artículos. Ésta vez lo haré al revés que hice con los principios SOLID, iré uno a uno y una vez los tenga todos escribiré un artículo que los aglutine y les de, en su conjunto, una razón de ser. Lo voy a hacer así porque ya tengo hechos ejemplos buenos y malos para cada principio y voy a ir más rápido. Me encantaría recibir feedback de los lectores, sobretodo de estudiantes de informática y desarrolladores que encuentren útiles mis explicaciones o que quieran que aclare o haga más hincapié en algún tema.

¿Qué es cohesión y acoplamiento?

El grado de cohesión mide la coherencia de la clase, esto es, lo coherente que es la información que almacena una clase con las responsabilidades y relaciones que ésta tiene.

El grado de acoplamiento indica lo vinculadas que están unas clases con otras, es decir, lo que afecta un cambio en una clase a las demás y por tanto lo dependientes que son unas clases de otras.

Como se ve claramente, los conceptos de cohesión y acoplamiento están íntimamente relacionados. Un mayor grado de cohesión implica uno menor de acoplamiento. Maximizar el nivel de cohesión intramodular en todo el sistema resulta en una minimización del acoplamiento intermodular. 

Alta cohesión

Nos dice que la información que almacena una clase debe de ser coherente y debe estar, en la medida de lo posible, relacionada con la clase. Los puritanos y teóricos diferencian 7 tipos de cohesión:
  1. Cohesión coincidente: el módulo realiza múltiples tareas pero sin ninguna relación entre ellas.
  2. Cohesión lógica: el módulo realiza múltiples tareas relacionadas pero en tiempo de ejecución sólo una de ellas será llevada a cabo.
  3. Cohesión temporal: las tareas llevadas a cabo por un módulo tienen, como única relación el deber ser ejecutadas al mismo tiempo.
  4. Cohesión de procedimiento: la única relación que guardan las tareas de un módulo es que corresponden a una secuencia de pasos propia del producto.
  5. Cohesión de comunicación: las tareas corresponden a una secuencia de pasos propia del producto y todas afectan a los mismos datos.
  6. Cohesión de información: las tareas llevadas a cabo por un módulo tienen su propio punto de arranque, su codificación independiente y trabajan sobre los mismos datos. El ejemplo típico: OBJETOS
  7. Cohesión funcional: cuando el módulo ejecuta una y sólo una tarea, teniendo un único objetivo a cumplir.

Bajo acoplamiento

Es la idea de tener las clases lo menos ligadas entre sí que se pueda, de tal forma que, en caso de producirse una modificación en alguna de ellas, tenga la mínima repercusión posible en el resto de clases, potenciando la reutilización, y disminuyendo la dependencia entre las clases. También hay varios tipos de acoplamiento.

1. Acoplamiento de contenido: cuando un módulo referencia directamente el contenido de otro módulo. (hoy en día es difícil verlo, quizá es más fácil verlo en entornos de programación funcional)
2. Acoplamiento común: cuando dos módulos acceden (y afectan) a un mismo valor global.
3. Acoplamiento de control: cuando un módulo le envía a otro un elemento de control que determina la lógica de ejecución del mismo.

Pero basta de palabrería, pasemos a los ejemplos. Para éste primer principio tengo cuatro ejemplos malos y sólo uno bueno.

Primer ejemplo, "alto acoplamiento":
public static class Servicios
{
    private static IServicioEmail _servicioEmail;

    public static IServicioEmail EmailService
    {
        get
        {
            if (_servicioEmail == null)
            {
                _servicioEmail = new ServicioEmail();
            }
            return _servicioEmail;
        }
    }
}

public interface IServicioEmail
{
    void Send(string subject, string message);
}

public class ServicioEmail : IServicioEmail
{
    public void Send(string asunto, string mensaje) { }
}

public class UserController
{
    public void Registrar()
    {
        Servicios.EmailService.Send("asunto", "mensaje");
    }
}
¿Por qué hay alto acoplamiento aquí? Sencillo, UserController utiliza tanto Servicios como ServicioEmail y no es necesario que UserController sepa nada de la clase Servicios.

Segundo ejemplo, "Clase de lógica de negocio que hace algo":
public class ClaseDeLogicaDeNegocio
{
    public void DoSomething()
    {
        // coge parámetros de configuración
        var umbral = int.Parse(ConfigurationManager.AppSettings["umbral"]);
        var connectionString = ConfigurationManager.AppSettings["connectionString"];

        // Vamos a por datos...
        var sql = @"select * from cosas like parametro > ";
        sql += umbral;

        using (var connection = new SqlConnection(connectionString))
        {
            connection.Open();
            var command = new SqlCommand(sql, connection);
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    var nombre = reader["Nombre"].ToString();
                    var destino = reader["Detino"].ToString();

                    // Haz algo más de lógica de negocio por otro lado...
                    HacerMasLogicaDeNegocio(nombre, destino, connection);
                }
            }
        }
    }
    public void HacerMasLogicaDeNegocio(string nombre, string destino, SqlConnection conexion)
    {
    }
}
¿No huele un poco mal éste código? Confieso: mis primeras aplicaciones en .NET se parecían mucho a ésto, quizá demasiado, y estoy seguro de que queda código mío altamente acoplado funcionando todavía... De la experiencia se aprende, ¿no?

Tercer ejemplo, "Saludar en inglés":
class AltoAcomplamiento
{
    public void SaludarEnIngles(string type)
    {
        switch (type)
        {
            case "GM":
                Console.WriteLine("Good Morning");
                break;
            case "GE":
                Console.WriteLine("Good Evening");
                break;
            case "GN":
                Console.WriteLine("Good Night");
                break;
        }
    }
}
class AltoAcoplamiento2
{
    public AltoAcoplamiento2()
    {
        var ejemplo = new AltoAcomplamiento();
        ejemplo.SaludarEnIngles("GM");
    }
}
Quizá es demasiado típico, quizá debería haber sido el primer ejemplo, no sé. El caso es que la clase AltoAcoplamiento2 necesita conocer la lógica interna de la clase AltoAcoplamiento. ¡Es muy feo!

Último ejemplo, "alto acoplamiento por vagancia":
class Mensaje
{
    private string _para;
    private string _asunto;
    private string _mensaje;

    public Mensaje(string to, string subject, string message)
    {
        _para = to;
        _asunto = subject;
        _mensaje = message;
    }
    public void Enviar()
    {
        // envia el mensaje...
    }
}
En un futuro alguien pide que se haga login del usuario antes de enviar el mensaje...
Y un mal desarrollador hace lo siguiente:
class MensajeVersion2
    {
        private string _para;
        private string _asunto;
        private string _mensaje;
        private string _nombreUsuario;

        public MensajeVersion2(string to, string subject, string message)
        {
            _para = to;
            _asunto = subject;
            _mensaje = message;
        }

        public void Enviar()
        {
            // envia el mensaje...
        }

        public void Login(string nombreUsuario, string contraseña)
        {
            _nombreUsuario = nombreUsuario;
            // code to login
        }
    }
El anterior código incumple varios de los principios SOLID, al menos la S y la O... (el resto no es aplicable en éste ejemplo por su simplicidad pero apostaría a que éste desarrollador incumpliría alguno más). Como decía, sólo voy a poner un ejemplo bueno, que va a ser la solución buena a éste último ejemplo:
public interface IServicioEmail
{
    void Enviar(string asunto, string mensaje);
}
public interface IServicioLogin
{
    void Login(string nombreUsuario, string contraseña);
}

public interface IUserController
{
    void Registrar();
}

public class UserController : IUserController
{
    private readonly IServicioEmail _servicioEmail;
    private readonly IServicioLogin _servicioLogin;

    public UserController(IServicioEmail servicioEmail, IServicioLogin servicioLogin)
    {
        // Se harían las debidas comprobaciones y después:
        _servicioEmail = servicioEmail;
        _servicioLogin = servicioLogin;
    }

    public void Registrar()
    {
        _servicioEmail.Enviar("asunto", "mensaje");
        _servicioLogin.Login("usuario", "contraseña");
    }
}
Creo que se ve claramente que cada clase/interfaz cumple un único cometido (S) y que UserController está abierto a la extensión pero cerrada al cambio (O) aunque quizá debería llamarse UserRegistrer. Doy por hecho que la jerarquía de las implementaciones de los servicios cumplirán con el principio de sustitución de Liskov (L). Se ve claramente también como hay segregación de interfaces (I) e inversión (inyección) de dependencias (D). Al hacerlo bien hemos conseguido aplicar y cumplir todos los principios SOLID y ya sabéis lo que dicen de SOLID:


¡SOLID rocks!

Juan García Carmona

6 sept. 2012

Introducción a los Tests Unitarios, TDD y Mocking

"El testing puede probar la presencia de errores pero no la ausencia de ellos."
Edsger Wybe Dijkstra

El presente artículo pretende ser una gran introducción a test unitarios y TDD y una breve introducción a Mocking.

La presencia de tests no garantiza la ausencia de errores a no ser que se cubran todos los caminos posibles del algoritmo o del proceso y de ahí la importancia de la cobertura de código, a mayor cobertura de código mayor seguridad de que no va a haber ninguna casuística “dramática”, es decir, algún caso no contemplado que haga que todo o parte de nuestro software “se rompa”. 

Antes de llegar a los tests unitarios y de pasar al Mocking voy a hacer un repaso de algunos conceptos generales relacionados con pruebas de software.

Test de validación / Tests de aceptación
Wikipedia: Proceso de revisión de Que el software producido cumple con las especificaciones de cliente y cumple su cometido. Se Valida Que lo Que se ha conseguido es realmente lo Que el usuario realmente quería . La pregunta a realizarse es: Es esto lo que el cliente quiere?
Ahora mismo lo que generalmente se hace en grandes proyectos tener a un grupo de testers probando casos de uso, es decir, viendo si se cumplen todos los requisitos en cada paso del caso de uso, entendiendo por caso de uso a una descripción detallada de una o varias funcionalidades.

Test de funcionalidad
Wikipedia: Una prueba funcional es una prueba basada en la ejecución, revisión y retroalimentación de las funcionalidades previamente diseñadas para el software. Las pruebas funcionales se hacen mediante el diseño de modelos de prueba que buscan evaluar cada una de las opciones con las que cuenta el paquete informático.
 Test de integración
Wikipedia: Se refieren a la prueba o pruebas de todos los etementos individuales que componen un proceso de una sola vez, así pues, los módulos individuales son combinados y probados en conjunto. Son las pruebas posteriores a las pruebas unitarias de cada módulo y preceden a las pruebas del sistema. 
Prueban varios componentes en conjunto. Hoy en dia son las pruebas más habituales y también pueden implementarse como tests unitarios aunque no siempre son deterministicos o repetibles, porque tienen demasiadas variables en juego, y no siempre son "rápidos", de hecho casi nunca lo son. 

Test unitario
Wikipedia: En programación, una prueba unitaria es una forma de probar el correcto funcionamiento de un módulo de código. Esto sirve para asegurar que cada uno de los módulos funciona correctamente por separado. Luego, con las pruebas de integración, se podrá asegurar el correcto funcionamiento del sistema o subsistema en cuestión. 
La idea es escribir casos de prueba para cada función no trivial o método en el modulo de forma que cada caso sea independiente del resto. No entendamos módulos como clases enteras, incluso ni siquiera como métodos, dentro de un mismo método, uno que contenga varios bucles, se podría definir  todo un conjunto de pruebas cuyo objetivo sea verificar que dado unas precondiciones o unos valores iniciales se cumplen unas post-condiciones, es decir, unas salidas o unos estados finales. Lo detallaré un poco más adelante cuando hable de la metodología AAA de tests unitarios. Como ya se ha dicho éstos son los tests más básicos y en ellos sólo se prueba o una clase aislada o interacciones entre clases o bloques pequeños de funcionalidad y deberían cumplir siempre las siguientes características:
  • Deben ser repetibles y determinísticos, es decir, ante unos inputs debo ser capaz de determinar los outputs SIEMPRE, porque se los requisitos y no escribo código a lo loco a ver que sale, y si ante determinadas situaciones sé que voy a tener determinados resultados ésto va a suceder y se va a repetir siempre
  • Deben (o deberian) se automáticos, Visual Studio y/o Team Foundation Server se encargan de ello en entornos Microsoft y sé que otros entornos también incluyen frameworks para pasar baterías de tests unitarios.
Test Driven Development

Hay que entender bien para que están pensados los tests unitarios y no probar partes del código a lo loco y es aquí donde entra TDD. TDD significa Test Driven Development, es decir, desarrollo guiado por pruebas. Es realmente sencillo explicarlo y todos lo entenderán aunque no sean programadores porque son tres pasos nada más.

1º Defines una prueba en la que se pide cumplir cierto requisito.
2º Escribes el código que hace que se cumpla ese requisito.
3º Ejecutas la prueba, si falla es porque el código está mal, si pasa puedes atacar el siguiente requisito.

A grandes rasgos es así de sencillo aunque el bucle en nuestro trabajo es un poco mas complejo aunque el siguiente gráfico lo expresa a la perfección:

 

Como vemos, las pruebas unitarias fomentan que el desarrollador modifique el código para mejorar su estructura lo que se ha llama actualmente refactorización. Al hacer TDD el desarrollador se ciñe más a los requisitos y no adelanta trabajo, cosa que suele degenerar en código decadente y zonas de código inutilizadas. 

Algo muy importante que poca gente menciona es que se puede documentar el código mediante pruebas unitarias. Como he dicho, una prueba unitaria verifica que se cumple un requisito, al elegir bien la nomenclatura de las pruebas unitarias podremos saber a la perfección como se comporta un servicio en nuestro sistema leyendo tan sólo lo títulos de cada una de las pruebas que cubren el código de dicho servicio.

Más adelante haré una o varias demostraciones de TDD para que se vean los patrones AAA y la nomenclatura When-ItShould que nos ayudarán a escribir tests más limpios y a documentar mediante tests respectivamente.

¿Qué aportan los tests unitarios y TDD a nuestros proyectos de Software?


Quizá sería más corto preguntar qué cosas no aportan los tests unitarios y el TDD porque, despues de lo expuesto hasta aquí vemos que los tests unitarios:
  • Reducen el número de errores y bugs ya que éstos, aplicando TDD, se detectan antes incluso de crearlos.
  • Facilidad para entender el código, ya que eligiendo una buena nomenclatura los tests sirven de documentación.
  • Facilidad para mantener el código ya que proveen de una "malla de seguridad" que nos protege ante cambios, pues, un error que surga al aplicar un cambio debería ser detectado (y corregido) antes de subir ese cambio. 
  • Ayudan a evitar regresiones, es decir, rollbacks por subidas de código que rompen algo. 
  • Dan confianza, gracias a esa malla de seguridad 
  • Facilidad para desarrollar si se aplica TDD pues nos ceñimos a los requisitos.
  • Ayudan a encontrar inconsistencias en los requisitos 
  • Ayudan a especificar comportamientos
  • Ayudan a refactorizar para mejorar la calidad de nuestro código (Clean code!)
  • A medio/largo plazo aumentan (mucho) la productividad.
  • Simplifican la integración de sistemas pues permiten llegar a la fase de integración con la seguridad de que cada módulo independiente cumple con cierto grado de calidad y está funcionando como debería, además reducen drásticamente los problemas y los tiempos dedicados a la integración porque nos permiten poder probar o depurar las relaciones de un módulo con el sistema sin necesidad de disponer del sistema completo. Ésto se logra simulando o falseando comportamientos de clases enteras con lo que se conoce como Mocking.  
Y todo esto repercute en lo que al cliente más le importa, el ROI, Return Of Investment en inglés o retorno de la inversión en castellano, es decir, en si ha invertido todo éste dinero en un software que vale para algo o no y, sobretodo, el tiempo que va a tardar en amortizar ésta inversión.

Todo ésto está muy bien, ¿verdad? ¿A alguien que no conociera nada sobre tests unitarios y TDD no le han llamado la atención? Pero ¡ojo! siempre hay que considerar, como decía al principio de éste artículo, que los tests:
  1. No garantizan el éxito de un proyecto
  2. No aseguran 100% de calidad
  3. No son aplicables a todo proyecto, ojalá lo fueran y vemos avances en ese sentido cada día.
  4. Obligan a pensar antes de empezar a programar! (cosa que se debería hacer siempre pero que la experiencia me dice que no es tan habitual...) 
Mitos sobre los tests unitarios:

"Escribir pruebas unitarias es escribir el doble de código."
Las pruebas siempre se deben ir escribiendo a medida que se desarrolla el software (TDD). A medida que desarrollamos vamos probando nuestro código lo que nos permite asegurar que el módulo queda terminado correctamente, libre de errores. Escribimos monos código, os lo aseguro.

"Desarrollar pruebas unitarias hace que los tiempos de desarrollo Se incrementen, incrementando I así los costes del proyecto."
Entre desarrollar sin tests y con tests, sinceramente, es más rápido, a priori, desarrollar sin probar nada... Lo hemos hecho hasta ahora, ¿no? (podria estar de acuerdo con esto en un desarrollo de andar por casa... ) pero entre desarrollar con pruebas unitarias y probar con métodos "mas rústicos", ésta segunda manera es más lenta y mas cara. La manera más rápida de desarrollar software es haciéndolo bien, si no desarrollamos código de calidad y no realizamos pruebas, los tiempos de desarrollo se podrán disparar enormemente porque se incrementa el tiempo de integración, los tiempos de depuración y el tiempo resolviendo incidencias, estabilizando el sistema, corrigiendo de bugs, etcétera. Además, muchos productos tienen un mantenimiento evolutivo que implica el desarrollo de nuevas funcionalidad y la corrección de incidencias. Sin un proceso adecuado de pruebas unitarias, los tiempos de pruebas se dispararan en fases posteriores, ya que será necesario probar y corregir tanto las nuevas funcionatidades como las funcionalidades ya existentes ante cada cambio. ¿Cuántas veces alguien ha subido algo y al cabo de dos días alguien se da cuenta de que hay un fallo en otra parte del sistema por culpa de aquella subida? 

lntegración Continua

Al hilo de lo que estaba diciendo, el control de la calidad de nuestras subidas, hay que hablar de la integración continua.  No se deben confundir los tests de integración con el concepto de integración continua., ya he hablado antes de ésto sin llegar a llamarlo integración continua. 

Integración continua quiere decir que si un test que antes funcionaba falla tras una subida existe una regresión entre el ultimo commit o subida testeada y la actual. Cuantos menos commits se hagan entre el proceso de pasar todas las pruebas mejor pues será más fácil ubicar y resolver cualquier error que nuestro desarrollo haya provocado. Mi regla es sencilla, antes de subir hay que bajar la última versión del código por si algún compañero ha subido algo, y se deben pasar todas las pruebas en local. Si todos los tests pasan entonces podemos subir con bastante tranquilidad. Si trabajamos con TFS podemos configurarlo para que en cada subida se pasen de nuevo todos los tests.

Otra regla divertida y que motiva al equipo a asegurarse antes de subir su código es llevar un registro de quien ha roto la compilación y hacerlo de una manera "cachonda", por ejemplo, mandando a todo el equipo una foto con la cara de quien acaba de romper la compilación o dejarle en su escritorio un set de globos de helio y una corona hasta que otro compañero "la cague" y rompa la compilación. ¿No suena divertida? ¿Quien ha dicho que programar fuera aburrido?

En un servidor de integración continua ideal (como TFS) deberíamos poder:
  • Programar la ejecución de tests "rápidos" (ante cada subida o cada hora durante la jornada laboral, eso dependerá del proyecto)
  • Programar tests "lentos" diariamente, tests enfocados a la integración o tests de estrés que sepamos que tardan demasiado tiempo. 
  • Obtener feedback de cada commit o subida en cuestión de minutos.
TDD: Metodología AAA  y nomenclatura When-ItShould

Hasta ahora he hablado mucho y he programado poco, vamos a ver con unos ejemplos prácticos de qué va todo ésto. Voy a empezar con el ejemplo típico de la calculadora. Supongamos que nos han pedido que hagamos una calculadora muy simple que sume dos números enteros naturales positivos a prueba de fallos. Como somos unos profesionales del TDD vamos a crear primero la clase CalculadoraTest y un primer método de prueba que instancie una calculadora y que comprueba que el resultado de sumar dos números, por ejemplo 2 y 2 es el que tiene que ser, es decir, 4. (repetible y determinista).
[TestClass]
public class CalculadoraTest
{
    [TestMethod]
    public void SumarTest1()
    {
        var calculadora = new Calculadora();
        var resultado = calculadora.Suma(2, 2);
        Assert.AreEqual(2 + 2, resultado);
    }
}
Lo primero que vemos es que éste código de test falla porque ni siquiera existe la clase Calculadora así que, como somos unos profesionales y practicamos los principios SOLID cada vez que programamos, vamos a empezar creando una interfaz ICalculadora (ISP) y una clase Calculadora que la implemente, ambas vacías. Ésto es a lo que en varias ocasiones hemos llamado refactorizar, ahora hemos hecho una refactorización partiendo desde la nada. Pero el código de pruebas sigue fallando, nos falta el método Suma así que lo vamos a crear en la interfaz y en la clase, vacío con, por ejemplo un "return -1;"
public interface ICalculadora
{
    int Suma(int num1, int num2);
}
public class Calculadora : ICalculadora
{
    public int Suma(int num1, int num2)
    {
        return -1;
    }
}
Si ejecutamos la prueba evidentemente va a fallar porque al sumar 2 + 2 nos va a devolver -1 y no va a cumplir la asertación, es decir, la verificación que hemos impuesto a nuestro test. ¿Qué hacemos ahora? Bueno, vamos a conseguir que pase el test, ¿no? Devolvamos num1 + num2.
public class Calculadora : ICalculadora
{
    public int Suma(int num1, int num2)
    {
        return num1 + num2;
    }
}
Ahora si ejecutamos el test el test pasa.


Bien, hemos avanzado pero nos quedan requisitos por cumplir, nos han pedido que sean dos números enteros positivos y a prueba de fallos y nos hemos quedado en "dos números enteros", nos queda el positivos y el a prueba de fallos, fallos que podrían venir de un desbordamiento pero vamos por pasos, nuestro siguiente requisito va a ser que sean positivos y vamos a seguir los requisitos a rajatabla, es decir, si nos llega un número negativo no nos vamos a inventar un mensaje ni un log, no, vamos a lanzar una excepción de argumentos. Empecemos con el primer número:
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SumarPrimerNumeroNegativo()
{
    var calculadora = new Calculadora();
    calculadora.Suma(-2, 2);
}
Si ejecutamos el test falla porque en nuestra clase no estamos controlando eso.


Habrá que refactorizarla para comprobar si el primer parámetro es menor que cero y lanzar una ArgumentOutOfRangeException. El código de nuestra clase quedará así:
public int Suma(int num1, int num2)
{
    if (num1 < 0)
        throw new ArgumentOutOfRangeException("num1");
    return num1 + num2;
}
Si ahora ejecutamos los tests pasan todos, estamos en verde.


 ¡Genial! Pues hagamos lo mismo con el siguiente parámetro, con num2, primero el test y luego el código que pasa el test:
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void SumarSegundoNumeroNegativo()
{
    var calculadora = new Calculadora();
    calculadora.Suma(-2, 2);
}
public int Suma(int num1, int num2)
{
    if (num1 < 0)
        throw new ArgumentOutOfRangeException("num1");
    if (num2 < 0)
        throw new ArgumentOutOfRangeException("num2");
    return num1 + num2;
}
Y si ejecutamos los tres tests pasan todos.


Como vemos éste proceso iterativo e incremental de testeo (TDD) es bueno pues, nos ceñimos a los requisitos sin inventarnos nada, cosa a la que tendemos todos los desarrolladores de forma innata cuando los requisitos no son claros. Voy a obviar todos los pasos pero voy a aabar los tests y la clase que se ciñe a los requisitos del ejemplo. Primero los tests:
[TestClass]
public class CalculadoraTest
{
    [TestMethod]
    public void SumarTest1()
    {
        var calculadora = new Calculadora();
        var resultado = calculadora.Suma(2, 2);
        Assert.AreEqual(2 + 2, resultado);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void SumarPrimerNumeroNegativo()
    {
        var calculadora = new Calculadora();
        calculadora.Suma(-2, 2);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void SumarSegundoNumeroNegativo()
    {
        var calculadora = new Calculadora();
        calculadora.Suma(2, -2);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void SumarPrimerNumeroEsMaximo()
    {
        var calculadora = new Calculadora();
        calculadora.Suma(int.MaxValue, 2);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void SumarSegundoNumeroEsMaximo()
    {
        var calculadora = new Calculadora();
        calculadora.Suma(2, int.MaxValue);
    }

    [TestMethod]
    [ExpectedException(typeof(ArithmeticException))]
    public void LaSumaSeSaleDelMaximo()
    {
        var calculadora = new Calculadora();
        calculadora.Suma(int.MaxValue-1, 2);
    }

    [TestMethod]
    public void LaSumaEsExactamenteElMaximo()
    {
        var calculadora = new Calculadora();
        calculadora.Suma(int.MaxValue - 1, 1);
    }
}
Después la clase que los pasa:
public class Calculadora : ICalculadora
{
    public int Suma(int num1, int num2)
    {
        
        if (num1 < 0)
            throw new ArgumentOutOfRangeException("num1");
        if (num2 < 0)
            throw new ArgumentOutOfRangeException("num2");
        if (num1 >= int.MaxValue)
            throw new ArgumentOutOfRangeException("num1");
        if (num2 >= int.MaxValue)
            throw new ArgumentOutOfRangeException("num2");
        try
        {
            int result = (Convert.ToUInt16(num1 + num2));
            return result;
        }
        catch(ArithmeticException ex)
        {
            throw new ArithmeticException("La suma se sale del máximo!",ex);
        }
    }
}


¿Es mejorable éste código? Pues seguro que si pero no es el objetivo de éste artículo aprender C# sino hacer una introducción al TDD y a Mocking. Observad que en todos los tests hemos hecho más o menos lo mismo, hemos creado y preparado el test, en este caso creando una calculadora y dando valor a las variables. Después hemos ejecutado lo que queríamos probar, en nuestro caso el método sumar, y después hemos verificado que el resultado de la prueba era el esperado. En todos los tests más o menos lo mismo, ¿cierto? Pues ¡bienvenido a la metodología AAA!

Metodologfa AAA: Arrange-Act-Assert

Es muy simple, lo acabamos de ver:
  1. Arrange: significa preparar y dar valores
  2. Act: es actuar, ejecutar lo que se quiere probar
  3. Assert: es asegurarse de que se ha hecho lo que se tiene que hacer
Hace unos años descubrí, gracias a Gary Mclean Hall, ésta manera de escribir tests unitarios y en verdad me ha facilitado la vida, a mí y a los compañeros que la han aprendido, interiorizado y aplicado en sus desarrollos. Voy a compartir en éste artículo su clase en aquel artículo, hecha mía y renombrada hacia la metodología AAA:

using Microsoft.VisualStudio.TestTools.UnitTesting;
/* *
 * 
 * Los tests unitarios tienen un comportamiento muy simple:
 * 
 * 1º Arrange()    -> Organizar las precondiciones
 * 2º Act()        -> Actuar, es decir, ejecutar lo que se quiere probar
 * 3º Assert()     -> Verificar que se han cumplido las postcondiciones
 * 
 * Esta visión simplista junto con una buena nomenclatura harán tu TDD mucho más sencillo.
 * 
 * Juan García Carmona
 * 
 * */

namespace ArrangeActAssert
{
    [TestClass]
    public abstract class UnitTestBase
    {
        [TestInitialize]
        public void Init()
        {
            Arrange();
            Act();
        }

        protected virtual void Arrange()
        {
        }

        protected abstract void Act();

        [TestCleanup]
        public void Cleanup()
        {
            System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown();
        }
    }
}

Ésta clase la tengo en un proyecto que se llama ArrangeActionAssert, es un proyecto que incluyen desde hace tiempo todos mis proyectos de tests. AAA y When-ItShould están intimamente relacionados, When-ItShould significa, literalmente, Cuando-Debería. Cuando son las dos primeras dos aes, que en palabras de Gary se puede traducir como Given That y When, es decir, nuestras tres aes son GivenThat-When-ItShould. Es lo mismo y quizá mas intuitivo, por ejemplo y al hilo del ejemplo anterior:

"Cuando sumo dos números enteros positivos debería obtener la suma de ambos."

Con la nomenclatura When-ItShould se puede cubrir toda la lógica de un algoritmo complejo y
documenta por si sola porque, tan solo con ver los nombres de los tests que cubren cierta
funcionalidad, se puede entender que hace y ver toda la lógica de negocio.

Veamos como quedaría el ejemplo de la calculadora utilizando AAA como clase base de nuestros tests y la nomenclatura When-ItShould...

Ésta sería la primera aproximación en UML (no ha quedado exactamente así pero sólo quiero que se vea la intención):

Éste sería el código de pruebas:

[TestClass]
public abstract class WhenUsingTheCalculator : UnitTestBase
{
    protected Calculadora Calculator;

    protected override void Arrange()
    {
        base.Arrange();
        Calculator = new Calculadora();
    }
}


[TestClass]
public abstract class WhenAddingTwoNumbers : WhenUsingTheCalculator
{
    protected abstract int X { get; }
    protected abstract int Y { get; }
    protected int result;

    protected override void Act()
    {
        result = Calculator.Suma(X, Y);
    }
}

[TestClass]
public class WhenAddingTwoPositiveNumbers : WhenAddingTwoNumbers
{
    protected override int X { get { return 13; } }
    protected override int Y { get { return 45; } }

    [TestMethod]
    public void ItShouldReturnTheCorrectResult()
    {
        Assert.AreEqual(58, result);
    }
}

[TestClass]
public class WhenTheFirstNumberIsLessThanZero : UnitTestBase
{
    // ARRANGE:
    protected  int X { get { return -1; } }
    protected int Y { get { return 1; } }
    protected int result;
    protected Calculadora Calculator;

    protected override void Arrange()
    {
        base.Arrange();
        Calculator = new Calculadora();
    }

    // ACT:
    // No queremos actuar aquí porque queremos recuperar una excepción:
    protected override void Act(){}

    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void ItShouldThrowAnOutOfRangeException()
    {
        // Actuamos dentro del Test
        result = Calculator.Suma(X, Y);
    }
}

[TestClass]
public class WhenTheSecondNumberIsLessThanZero : UnitTestBase
{
    // ARRANGE:
    protected int X { get { return 1; } }
    protected int Y { get { return -1; } }
    protected int result;
    protected Calculadora Calculator;
    protected override void Arrange()
    {
        base.Arrange();
        Calculator = new Calculadora();
    }

    // ACT:
    // No queremos actuar aquí porque queremos recuperar una excepción:
    protected override void Act() { }

    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void ItShouldThrowAnOutOfRangeException()
    {
        result = Calculator.Suma(X, Y);
    }
}

[TestClass]
public class WhenTheFirstNumberIsOutOfRange : UnitTestBase
{
    // ARRANGE:
    protected int X { get { return int.MaxValue; } }
    protected int Y { get { return 1; } }
    protected int result;
    protected Calculadora Calculator;

    protected override void Arrange()
    {
        base.Arrange();
        Calculator = new Calculadora();
    }

    // ACT:
    protected override void Act() { }

    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void ItShouldThrowAnOutOfRangeException()
    {
        result = Calculator.Suma(X, Y);
    }
}

[TestClass]
public class WhenTheSecondNumberIsOutOfRange : UnitTestBase
{
    // ARRANGE:
    protected int X { get { return 1; } }
    protected int Y { get { return int.MaxValue; } }
    protected int result;
    protected Calculadora Calculator;

    protected override void Arrange()
    {
        base.Arrange();
        Calculator = new Calculadora();
    }

    // ACT:
    protected override void Act() { }

    [TestMethod]
    [ExpectedException(typeof(ArgumentOutOfRangeException))]
    public void ItShouldThrowAnOutOfRangeException()
    {
        result = Calculator.Suma(X, Y);
    }
}

[TestClass]
public class WhenTheTheSumIsOutOfRange : UnitTestBase
{
    // ARRANGE:
    protected int X { get { return 2; } }
    protected int Y { get { return int.MaxValue-1; } }
    protected int result;
    protected Calculadora Calculator;
    protected override void Arrange()
    {
        base.Arrange();
        Calculator = new Calculadora();
    }

    // ACT:
    protected override void Act() { }

    [TestMethod]
    [ExpectedException(typeof(ArithmeticException))]
    public void ItShouldThrowAnArithmeticException()
    {
        result = Calculator.Suma(X, Y);
    }
}

[TestClass]
public class WhenTheTheSumIsExactlyMaxValue : UnitTestBase
{
    // ARRANGE:
    protected int X { get { return 1; } }
    protected int Y { get { return int.MaxValue - 1; } }
    protected int result;
    protected Calculadora Calculator;

    protected override void Arrange()
    {
        base.Arrange();
        Calculator = new Calculadora();
    }

    // ACT:
    protected override void Act() { }

    [TestMethod]
    public void ItShouldReturnMaxValue()
    {
        result = Calculator.Suma(X, Y);
        Assert.AreEqual(int.MaxValue, result);
    }
}

Creo que ese código es auto explicativo  y las pruebas lo son aún más, quizá es demasiado largo y se podría simplificar mucho, por ejemplo, haciendo algo muy común que es hacer Arrange y Act dentro del método de test en pruebas que esperan excepciones. Que no se pierda nadie, en la clase WhenAddingTwoNumbers podría haber metido, sueltos, cada uno de los tests de errores y haberlos llamado por ejemplo  ItShouldReturnMaxValueGivenThatTheSumIsExactlyMaxValue y dentro de ese método de prueba podría haber hecho:

result = Calculator.Suma(1, int.MaxValue -1);
Assert.AreEqual(int.MaxValue, result);

Además, al escribir éstas pruebas me he dado cuenta, porque una de ellas me fallaba, que mi calculadora no se estaba comportando bien entorno al valor máximo. Al final he refactorizado el código de la calculadora y ha quedado así:
public class Calculadora : ICalculadora
{
    public int Suma(int num1, int num2)
    {
        if (num1 < 0)
            throw new ArgumentOutOfRangeException("num1");
        if (num2 < 0)
            throw new ArgumentOutOfRangeException("num2");
        if (num1 >= int.MaxValue)
            throw new ArgumentOutOfRangeException("num1");
        if (num2 >= int.MaxValue)
            throw new ArgumentOutOfRangeException("num2");
        try
        {
            var result = num1 + num2;
            if (result < 0)
                throw new ArithmeticException("La suma se sale del máximo!");
            else
            return result;
        }
        catch(ArithmeticException ex)
        {
            throw new ArithmeticException("La suma se sale del máximo!",ex);
        }
    }
}

¿Sigue el lector conmigo o se ha marchado asustado?

Cobertura

Antes de entrar en temas de Mocking, parte importante de éste artículo, y para relajarnos un poco quiero volver al principio:
"El testing puede probar la presencia de errores pero no la ausencia de ellos."
Ya nos ha quedado claro a todos que la presencia de tests no garantiza la ausencia de errores a no ser que se cubran todos los caminos posibles de nuestros algoritmos, y de ahí la importancia de la cobertura de código. A mayor cobertura, mayor seguridad de que no va a haber ninguna causistica "dramática", algún caso no contemplado que haga que todo o parte de nuestro software "se rompa". (Lo sé, me repito, pero es importante)

Se llega a la practica certeza de que no hay errores cuando se han probado todas los posibles caminos de un algoritmo. La cantidad de caminos depende de la complejidad de cada algoritmo y dicha complejidad se mide con lo que se conoce como complejidad ciclomática.

Complejidad ciclomática
Wikipedia: La complejidad ciclomática (en inglés, cyclomatic complexity) es una métrica del software que proporciona una medición cuantitativa de la complejidad lógica de un programa. Es una de las métricas de software de mayor aceptación, ya que ha sido concebida para ser independiente del lenguaje.
Se utiliza, entre otras cosas para planificación de pruebas: el análisis matemático ha
demostrado que la complejidad ciclomática indica el número exacto de casos de prueba
necesarios para probar cada punto de decisión en un programa.

Siendo:

  • CC = complejidad ciclomatica
  • A = Número de aristas del grafo. Una arista conecta dos vértices si una sentencia puede ser ejecutada inmediatamente después de la primera.
  • N = Número de nodos del grafo correspondientes a sentencias del programa.
  • P = Número de componentes conexos correspondientes a las diferentes subrutinas, funciones o métodos.

Entonces: CC = A - N + P

No voy a entrar en ejemplos de cálculo de la complejidad ciclomática pero si alguien quiere saber más aquí dejo éste enlace.

Ahora pasemos, por fin, a Mocking...

¿Qué es Mocking?

Es Una técnica para testar software en la que se simulan partes del sistema con las que el objeto a testar tiene alguna dependencia. Un mock es un objeto "dummy", son "fakes" de partes del sistema a las que se puede definir su comportamiento "On the fly". ¿Cómo es posible ésto? Fácil, un mock usa la interfaz de un objeto real y la implementa y si hemos seguido mínimamente los principios SOLID podemos sustituir una instancia de nuestro objeto falseado, nuestro mock, por el objeto que en realidad se esperaba.

Repito, el objeto que queremos probar utiliza un objeto "mockeable", y lo es porque implementa cierta interfaz que define su comportamiento. Al objeto testeable no le importa en realidad cual de los dos objetos usa, no sabe si es una implementación real o es un fake. En el fondo no deja de ser polimorfismo aplicado mediante inyección o inversión de dependencias.

  1. DI obliga a separar componentes
  2. Para testar hace falta aislar módulos
  3. Aislar módulos implica quitar dependencias
  4. Mocking permite simular otros módulos

Ergo :

Mocking Rocks!!

Éste artículo quería ser una introducción a TDD y Mocking y no un artículo sobre Mocking, por eso, después de varios días preparandolo acabo de decidir dejar para un siguiente artículo el Mocking propiamente dicho, en castellano, claro, pero voy a recomendar alguna lectura que estoy seguro que puede servir de guía para quien haya leído hasta aquí y no pueda esperar al siguiente aertículo.


Como comentario final quiero insistir en que merece la pena tener siempre presentes los principios SOLID y GRASP o principios para la asignación de responsabilidades, los patrones de diseño y en concreto la inyección de dependencias (DI) y el principio en el que se basa, la inversión del control (loC). Tener estos conceptos claros nos facilitará el testing mediante Mocking y en general, dichos principios nos aportarán profesionalidad desarrollando software. Aún tengo pendiente una serie de artículos sobre GRASP y otra serie de artículos sobre patrones de diseño entre los que está DI, todos ellos con ejemplos de código y diagramas UML realizados por mí. En concreto para el de patrones de diseño quería hacerlo, y ya lo he empezado, utilizando TDD y mocking. Tiempo al tiempo.
Juan García Carmona