Gracias a que el mundo del desarrollo de software es una “especie en continua evolución”, hemos conseguido pasar del desarrollo de software “en cascada” (o “en barrena”, según se mire) que aprendí cuando hace años sólo tenía que preocuparme por estudiar a cosas tan interesantes y buenas para la calidad de los programas como el desarrollo basado en pruebas (o Test Driven Development, TDD) que muy básicamente se puede definir como desarrollar unas pruebas que han de cumplirse para verificar que un programa o sus partes hacen lo que deben hacer (y nada más) correctamente.
El hecho de que el TDD es un éxito y algo que se demuestra cada día más necesario para la garantizar la calidad del software se puede ver en muchas partes, como en que el TDD forme parte del grupo de técnicas de Xtreme Programming (XP), metodología de desarrollo TIC que forma parte del conjunto de Metodologías Ágiles del que tanto se habla ahora (si queréis saber más sobre todo esto, seguirnos en nuestra Web, dentro de poco tendréis novedades al respecto!) o que talleres sobre este tema como los que Carlos Segura de NavarraDotNet ha impartido en los CES hayan sido un éxito.
No obstante, el TDD como todo presenta algunas dificultades, crear y mantener un buen conjunto de pruebas puede ser una gran dificultad en sí mismo. Para empezar, se necesita disponer de una (varias mejor) persona que tenga esa “mentalidad" y visión del concepto general de las pruebas, algo que a veces no se ve tan fácilmente. Luego, podemos tener que escribir mucho más código de pruebas que el código propio de nuestro programa, y ese código para pruebas también hay que mantenerlo :( Además, escribir pruebas para código muy interrelacionado es todo un reto que requiere bastante conocimiento. Con los Mocks y los Stubs podemos solucionar esto de alguna manera, pero claro, nos hace falta conocer cómo funcionan los Mocks y los Stubs… En fin, que el TDD bueno es, pero sencillo-sencillo como que no.
¡Pero aquí es donde acuden los chicos de Microsoft Research al rescate! Microsoft Research es la división del gigante que se ocupa de pensar los futuros productos, desarrollos o servicios que podrán (o no) ver la luz desde Redmond. Se trata de 8 grupos de trabajo distribuidos por todo el mundo, donde se desarrollan algunos de los proyectos más interesantes de la empresa. Llevan desde 1991 desarrollando sus proyectos y este sí que es uno de los grupos de Microsoft que me encantaría conocer personalmente.
Dentro de Microsoft Research, Nikolai Tillmann y Peli de Halleux han desarrollado una herramienta que puede ser la solución a nuestras dificultades con el TDD: Pex.
Pex es una herramienta pensada para automatizar el desarrollo de un conjunto de pruebas para nuestro código. Directamente desde el editor de Visual Studio (en alguna versión Team, eso sí, aunque hay versiones “command line” incluso para VS Express) Pex busca posibles valores de entrada y salida para nuestros métodos, que pueden salvarse para generar pruebas simples de nuestro código, pero lo hace con una cobertura muy amplia del mismo (es decir, cubre o comprueba casi todas las ramas y bifurcaciones que existen en el código). Pex hace esto de manera sistemática, buscando condiciones de límites, excepciones y fallos de validación que se pueden “debuggear” directamente desde el IDE. El concepto que usa Pex es lo que denominan Parameterized Unit Testing, una extensión del Unit Testing que lo que hace es apoyarse en una plantilla para llamar a los test pasándoles varios parámetros de manera que un mismo código de test nos vale para varias comprobaciones. La verdad es que desde mi punto de vista personal, Nikolai y Peli han hecho un trabajo excelente, que aunque quizá no esté acabado ya 100% nos permite desde ya poder desarrollar tests unitarios y ejecutarlos en nuestro código con esa técnica tan complicada de “botón derecho-siguiente-siguiente-siguiente” :) Y si queremos poner un proyecto en producción a tiempo y con calidad no podemos pedir mucho más que esto.
Instalación de Pex
Tras descargar Pex desde su web, simplemente ejecutamos el paquete de instalación y es muy sencillo de instalar, elegimos la instalación “Typical” y listo.
Si en la última pantalla dejamos marcada la opción “Getting started with Pex” nos abrirá un pequeño documento en formato PDF con unos ejemplos muy buenos de cómo usarlo, que es lo que veremos a continuación.

Primeros pasos con Pex
Lo primero que hacer es crearnos un poco de código de prueba. El PDF mencionado nos da uno suficientemente útil, así que lo haremos con ese. Una vez editado, hacemos click con el botón derecho sobre cualquier zona del código y elegimos “Run Pex”. Nos presentará una pantalla donde se nos ofrece la elección del modelo de tests que queremos generar, en nuestro caso elegimos Visual Studio y aceptamos.
Tras esto podemos ejecutar la opción de “Run Pex Explorations” que lo que hace es lanzar Pex para que automáticamente genere diversos casos de prueba de nuestro código de acuerdo a diversos valores para los argumentos de entrada. Estos valores no son aleatorios sino que mantienen una cierta lógica de manera que por ejemplo para una cadena podamos meter ninguno, uno o más caracteres, en mayúsculas y minúsculas, etc. Pex es además capaz de reconocer posibles valores que tienen cierto efecto particular en nuestro código, para probarlos también como parámetros de entrada.
La pantalla de resultados de Pex nos muestra los resultados de la ejecución de los diversos tests sobre un proceso PUT para test del código, con los diversos valores de los parámetros que se nos muestran en la tercera columna. A la izquierda del todo podemos ver gráficamente qué ha pasado: verde bien, rojo mal.
Los números de la segunda columna nos sirven para identificar los test en el código como veremos más adelante. En nuestro ejemplo vemos que hay un test no pasado, que es la comprobación de la cadena vacía.
Pex nuevamente demuestra ser una herramienta endiabladamente útil ya que directamente desde su explorador de resultados podemos corregir este fallo. Si seleccionamos el error en cuestión a la derecha veremos un pequeño conjunto de detalles, la pila de ejecución y unos botones entre los que está “Add Precondition…”
Si lo seleccionamos, Pex nos ofrece otra ventana donde detalla el código que va a introducir en el nuestro para solucionar el problema.
Seleccionamos Apply y ya podemos ejecutar de nuevo los tests para verificar que el fallo está solucionado. ¡Más sencillo sería difícil! También podemos si lo queremos permitir un fallo, enviarlo por correo, a fichero de texto o al portapapeles, todo pulsando con el botón derecho sobre el elemento en cuestión.
A continuación, deberíamos salvar nuestro test, con la opción Save. Pex nos presenta una ventana donde nos indica qué va a hacer. Por defecto nos creará un proyecto de pruebas (con extensión .Tests) asociado a nuestra solución, con todas las referencias y demás elementos necesarios.
También podemos optar por la opción Save All si tenemos todos los tests seleccionados. De esta manera podemos tener todos los tests a nuestra disposición para poder usarlo como un “regression test” en el futuro o para automatizar un “quick check” sobre nuestras futuras builts (mediante esa otra “pequeña” herramienta de Microsoft llamada Visual Studio Team Foundation Server, o con otras).
Pex crea el código de test con una estructura en la que del archivo <Nombre de nuestra clase>Test.cs cuelgan una serie de archivos .g.cs que se corresponden con los métodos de la Clase en cuestión.
Si editamos el código en el archivo <Nombre de nuestra clase>Test.cs podemos ver el código del PUT:
Code Snippet
- // <copyright file="StringExtensionsTest.cs" company="Microsoft">Copyright © Microsoft 2010</copyright>
- using System;
- using Microsoft.Pex.Framework;
- using Microsoft.Pex.Framework.Validation;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using PEXTestClassLibrary1;
- namespace PEXTestClassLibrary1
- {
- [TestClass]
- [PexClass(typeof(StringExtensions))]
- [PexAllowedExceptionFromTypeUnderTest(typeof(ArgumentException), AcceptExceptionSubtypes = true)]
- [PexAllowedExceptionFromTypeUnderTest(typeof(InvalidOperationException))]
- public partial class StringExtensionsTest
- {
- [PexMethod]
- public string Capitalize(string value)
- {
- string result = StringExtensions.Capitalize(value);
- return result;
- // TODO: add assertions to method StringExtensionsTest.Capitalize(String)
- }
- }
- }
Como vemos, Pex emplea bastantes atributos para identificar su código. En concreto, aquí vemos que el PUT de Capitaliza no es más que un wrapper para llamar a nuestra función, pero este es el sitio donde podríamos introducir nuevas validaciones que personalizaran nuestro test en caso de necesitarlo. Es importante destacar que sólo aquí es donde Pex nos "permite” modificar su código.
Por su parte, el archivo <Nombre de nuestra clase>Test.<Nombre de método>.g.cs contiene todos los tests que Pex genera para el método:
Code Snippet
- // <copyright file="StringExtensionsTest.Capitalize.g.cs" company="Microsoft">Copyright © Microsoft 2010</copyright>
- // <auto-generated>
- // This file contains automatically generated unit tests.
- // Do NOT modify this file manually.
- //
- // When Pex is invoked again,
- // it might remove or update any previously generated unit tests.
- //
- // If the contents of this file becomes outdated, e.g. if it does not
- // compile anymore, you may delete this file and invoke Pex again.
- // </auto-generated>
- using System;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using Microsoft.Pex.Framework.Generated;
- namespace PEXTestClassLibrary1
- {
- public partial class StringExtensionsTest
- {
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize06()
- {
- string s;
- s = this.Capitalize("h\0");
- Assert.AreEqual<string>("H", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- [ExpectedException(typeof(ArgumentNullException))]
- public void Capitalize01()
- {
- string s;
- s = this.Capitalize((string)null);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize02()
- {
- string s;
- s = this.Capitalize("");
- Assert.AreEqual<string>("", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize03()
- {
- string s;
- s = this.Capitalize("\0");
- Assert.AreEqual<string>("", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize04()
- {
- string s;
- s = this.Capitalize(":");
- Assert.AreEqual<string>("_", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize05()
- {
- string s;
- s = this.Capitalize("a");
- Assert.AreEqual<string>("A", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize07()
- {
- string s;
- s = this.Capitalize("ha");
- Assert.AreEqual<string>("Ha", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize08()
- {
- string s;
- s = this.Capitalize("haa");
- Assert.AreEqual<string>("Haa", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize09()
- {
- string s;
- s = this.Capitalize(":\0");
- Assert.AreEqual<string>("_", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize10()
- {
- string s;
- s = this.Capitalize("::");
- Assert.AreEqual<string>("__", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize11()
- {
- string s;
- s = this.Capitalize("h:\u8061\0");
- Assert.AreEqual<string>("H_\u8061", s);
- }
- [TestMethod]
- [PexGeneratedBy(typeof(StringExtensionsTest))]
- public void Capitalize12()
- {
- string s;
- s = this.Capitalize("h::z:h:\0\0\0\0");
- Assert.AreEqual<string>("H__Z_H_", s);
- }
- }
- }
Lo primero que Pex nos dice claramente es no modificar este código. Aquí es donde vemos la correspondencia entre los números de la columna izquierda de la ventana “Pex Exploration Results” y el código ya que cada procedimiento está numerado correspondientemente. Está claro que Pex nos ha ahorrado mucho trabajo…
Code Coverage
Pex nos permite bastantes cosas más, podemos aumentar el límite de su exploración (si tenemos tiempo y una máquina lo suficientemente potente) para aumentar el “code coverage” de nuestros test, podemos ejecutar una prueba concreta en modo depuración con puntos de interrupción para ver su progreso (en la ventana Resultados de pruebas), etc.
Desde la ventana de Prueba/Ventana/Vista de pruebas de Visual Studio también podemos gestionar las pruebas a ejecutar del conjunto y hacer las tareas típicas de testing, como comprobar en el futuro si algún cambio que hemos introducido afecta negativamente al código.
Pex nos permite también examinar nuestro “code coverage”, como hemos comentado. No se trata de ala cobertura global de nuestra solución, ya que para esto deberíamos emplear las herramientas de Visual Studio Team Test, pero nos da una idea muy buena de la clase actual. Si vamos al menú Prueba> Editar configuraciones de ejecución de pruebas y seleccionamos la Ejecución de pruebas local para activar la cobertura de código. Seleccionamos en la ventana que aparece este elemento y activamos el artefacto en nuestro proyecto de librería (no en el proyecto de Test) comprobando que el checkbox de “Instrumentar ensamblados en contexto” está seleccionado:
Aplicamos y cerramos y ya está Visual Studio listo para volver a ejecutar las pruebas y comprobar la cobertura de código. Lo haremos esta vez desde la ventana de “Vista de pruebas”. Teniendo todas las pruebas seleccionadas, las ejecutamos y una vez que han concluido podemos pulsar el botón “Mostrar los resultados de la cobertura de código” en la parte más a la derecha de la ventana de “Resultados de pruebas” y nos abrirá a su vez otra ventana más abajo con los resultados del análisis de cobertura. Si los desplegamos podemos ver que en nuestro caso tenemos una cobertura del 100%, no está mal.
Los PUTs
Volviendo un poco al tema de los PUTs ya que es en estos donde podemos tener que realizar más ajustas y cambios para reproducir nuestros Tests, hemos dicho antes que el código en los archivos .g.cs no se debe modificar nunca, y sólo podemos hacerlo en el archivo del PUT. Por ejemplo, podemos añadir un nuevo test para validar que nuestro método sólo devuelve letras o subrayados:
Code Snippet
- [PexMethod]
- public void CapitalizeReturnsOnlyLettersAndUnderscores(string input)
- {
- string output = StringExtensions.Capitalize(input);
- PexAssert.TrueForAll(output, c => char.IsLetter(c) || c == '_');
- }
Si volvemos a ejecutar la exploración de Pex en todo el proyecto, o sólo en nuestra clase, y a continuación volvemos a “Ejecutar todas las pruebas…” (o si estamos en la vist a de código de los PUT podemos hacer botón derecho y “Ejecutar pruebas”) Pex incluirá nuestras nuevas pruebas en los resultados, como vemos a continuación:

Más información
Como hemos visto, Nikolai y Peli han hecho un gran trabajo (bueno, están en ello) que espero que nos ayude a todos a escribir mejor código muy fácilmente, y supongo que dentro de poco saldrá de los laboratorios de Microsoft Research para convertirse en parte soportada de Visual Studio. Si los quieres “conocer” personalmente tienen un video explicando todo esto en Channel9:
http://channel9.msdn.com/posts/Peli/Getting-started-with-Pex-in-Visual-Studio-2008/
Aprovechando el estreno de nuestro CESDigital 2.0 y lo fácil que se hace escribir artículos con Windows Live Writer ahora, voy a enlazar un libro de Amazon que es un referente totalmente recomendable en el mundo del Unit Testing:
Este libro se vendía “como churros” en el último Microsoft TechEd, no hay mucho más que añadir ;)