Cómo elegir tests de unidad

En mi artículo anterior os expliqué cómo escribir tests de unidad, pero pasé prácticamente por alto un tema muy importante: ¿qué tests de unidad escribimos? Por desgracia, el párrafo que le dediqué lo explicaba tan mal y de forma tan resumida, que mucha gente se llevó la idea equivocada, así que para solucionarlo aquí va un nuevo artículo dedicado exclusivamente a ese tema.


Por lo general, nuestro objetivo cuando escribimos tests de unidad es comprobar que esta unidad funciona correctamente en los casos normales. Fijaos en que he dicho “comprobar” y no “demostrar”: no queremos una demostración de que la unidad siempre produce una salida correcta con todas las posibles combinaciones de datos de entrada, sino una manera automatizada de comprobar que la implementación funciona bien.

Parece la misma cosa, pero no lo es, y la distinción es importante. Si quisiéramos demostrar que la implementación de un módulo de gestión de clientes es correcta, tendríamos que hacer un catálogo exhaustivo de posibles entradas correctas y de posibles entradas incorrectas, pasarlas todas por el módulo, y comprobar que producen las salidas esperadas. Esto puede suponer mucho tiempo y esfuerzo. Sin embargo, para comprobar que el módulo funciona, no tenemos que matarnos tanto, ya que las pruebas son más informales: grabar un registro, comprobar que se puede leer, modificarlo, comprobar que ahora se lee la versión modificada, borrarlo, ver qué pasa si se intenta obtener un registro que no existe, etc. Mientras que tenemos que asegurarnos de que probamos toda la funcionalidad, estas pruebas no necesitan ser totalmente exhaustivas. De este modo, evitamos que la creación y mantenimiento de los tests de unidad supongan una carga excesiva.

El nivel de detalle al que deberíamos llegar depende del tipo de unidad que queremos probar. Por ejemplo, para una biblioteca que va a ser utilizada por programadores externos, los tests son muy detallados, comprobando diversas entradas válidas y no válidas, etc. Sin embargo, para un módulo interno de una aplicación, los tests sólo cubrirán por lo general los casos que se encuentren en el funcionamiento normal del programa, sin pararse a considerar casos triviales o irrelevantes. En algunos casos no vale la pena escribir tests de unidad; por ejemplo, un “value object” que consista exclusivamente en funciones “get” y “set” triviales no necesita uno.

Los tests de Apache Harmony (una reimplementación libre de las bibliotecas estándar de Java) son un buen ejemplo del primer caso. Por ejemplo, echadle un ojo a ArrayListTest, y veréis que cada test comprueba un método de ArrayList diferente, llamándolo de diferentes maneras, y comprobando que producen los resultados esperados de acuerdo con la especificación.

Para un ejemplo del segundo caso, mirad en Apache Shindig (implementación de referencia de OpenSocial) la clase BasicOAuthStoreTest, que usa unos cuantos datos de ejemplo representativos de un funcionamiento normal del programa para comprobar que las funciones de BasicOAuthStore hacen lo que se supone que deben hacer, pero sin pretender que estas pruebas sean exhaustivas.

Espero que este artículo os haya despejado unas cuantas dudas, y nos veremos en la próxima entrega de la serie :)

(Primer artículo, siguiente artículo).