Categoría: Unit Tests

Pruebas unitarias fáciles de entender

Queremos que el código fuente del sistema sea fácil de entender para evitar retrasos y defectos, y las pruebas unitarias deberían cumplir con la misma meta. Veremos cinco recomendaciones:

  1. Organización por clases
  2. Organización con namespaces
  3. Cómo nombrar una prueba unitaria
  4. El patrón: Esperado, Obtenido y Comparación
  5. Organización de escenarios de prueba

Todos los ejemplos los puede leer completos en este repositorio: https://github.com/oscarcenteno/algoritmos.cs.garantias.

Organización por clases

Las pruebas unitarias de un método se ubican en una sola clase de pruebas unitarias. Recomiendo que nombremos las clases de pruebas unitarias como el método que prueban + “_Tests”. Agregando dicho sufijo evitamos errores frecuentes cuando los nombres producen conflictos de compilación.

Es decir, si probamos una propiedad llamada “AporteDeGarantia”, entonces su clase de pruebas unitarias se llama “AporteDeGarantia_Tests”, como en este ejemplo:

nombre-de-la-clase

 

De esta manera, logramos que todas las pruebas unitarias de un mismo método puedan leerse juntas y puedan compartir las mismas variables de instancia.

Organización con namespaces

Los mismos namespaces que nos ayudan a organizar los algoritmos que vamos a probar deberían existir en el proyecto de Pruebas Unitarias. Podemos decir que son un idénticos a nivel de namespaces y esto nos ayuda a encontrar fácilmente las pruebas de cualquier clase. Este es un ejemplo:

organizacion-por-namespaces

Cómo nombrar una prueba unitaria

Siguiendo la recomendación del libro “The Art of Unit Testing“, las nombramos con este patrón:

MetodoQueProbamos_Escenario_ResultadoEsperado

Especialmente en casos donde hay condicionales, en “Escenario” indicamos el caso que probamos. ResultadoEsperado explica en pocas palabras lo que esperamos recibir. Con esto, buscamos que cuando una prueba unitaria falle podamos ubicarnos fácilmente en cuál funcionalidad tiene un defecto:

ayuda-cuando-una-falla

El patrón: Esperado, Obtenido y Comparación

Este patrón logra una apariencia similar en todas las pruebas unitarias, y nos recuerda que toda prueba unitaria confiable tiene una comparación y solamente una.

esperado-obtenido-comparacion

Organización de escenarios de prueba

Cuando se da el caso que varias clases de pruebas unitarias tienen los mismos escenarios a partir de los cuales obtienen sus resultados, podemos extraer esos casos a una clase de “Escenarios”.

Esto nos ayuda a no duplicar código, y hace que todas las pruebas sean más concisas y claras. Los ejemplos anteriores utilizan una clase de Escenarios, y este es el contenido de parte de esta clase:

clase-de-escenarios

Puede leer todas las pruebas unitarias y sus escenarios en el repositorio: https://github.com/oscarcenteno/algoritmos.cs.garantias

Trabajando con las pruebas unitarias en un algoritmo con objetos

¿Cómo podemos identificar más fácilmente dónde se introdujo un defecto en un algoritmo? Durante la serie de Algoritmos Mantenibles, logramos crear un algoritmo como un árbol de objetos como el siguiente:

arbol-de-objetos

Los objetos de responsabilidad única nos permiten probar unitariamente cada rama del árbol. Esto contrasta con las pruebas de un procedimiento, en donde probamos una sola pieza grande. Cada uno de los  rectángulos en la siguiente imagen representadan las pruebas unitarias de cada objeto, con lo que vemos que tenemos una cobertura completa. Las pruebas de un objeto superior tambien ejercitan los objetos inferiores:

cobertura-de-pruebas-de-los-objetos

Cuando una prueba unitaria falla, estaremos seguros de que el error está confinado a uno de los rectángulos, por lo que será más fácil de corregir. Como se ilustra en la imagen, si una prueba unitaria de ApellidosFormateados falla (cuadro en rojo), sabremos que el área a revisar es la clase misma, pues las pruebas unitarias de ApellidosEnMayusculas están exitosas (el cuadro está azul). Además, revisaremos que estemos enviando los parámetros correctos al crear ApellidosFormateados.

cobertura-de-pruebas-de-los-objetos-falla-localizada

Por otro lado, si las pruebas unitarias de ambos ApellidosFormateados  y ApellidosEnMayusculas fallan, podemos ir al objeto más inferior pues allí es donde se introdujo un defecto, o revisar si cambiamos los parámetros con que creamos ApellidosFormateados.

falla-en-el-ultimo-objeto

Este es un de los motivos por los que es muy útil visualizar los llamados entre los objetos al programar. Si solamente vemos al software como archivos de texto duraremos más tiempo en identificar dónde se dan los errores.

El código fuente de este ejemplo lo podemos encontrar en este repositorio: https://github.com/oscarcenteno/algoritmos.cs.sujetos.

Este es una de las ventajes de la programación con objetos de responsabilidad única.

¿Cómo crear un proyecto de prueba unitarias (Unit Test Project)?

Veamos cómo se crea un Unit Test Project, el tipo de proyecto que puede contener pruebas unitarias.

YouTube

Office Mix

Playlist de Introducción al Unit Testing

¿Cómo analizar las pruebas unitarias?

Dentro del Visual Studio veremos cómo leer los resultados cuando una prueba unitaria no obtiene lo que espera. Esto es clave para que podamos corregir errores al programar.

Youtube
Office Mix

¿Qué es una prueba unitaria?

Esta es la primera parte de la serie de Introducción al Unit Testing. Empezamos definiendo qué son las pruebas unitarias y cómo ayudan a tener un software más fácil de probar.

Youtube
Office Mix