Creando fixtures de dominio legibles
En muchas ocasiones nos habremos encontrado en nuestros proyectos con la necesidad de testear capas aisladas que hacen uso de objetos de dominio, objetos que en tests de integración / producción serán creados y poblados por un framework ORM o similar.
Dichos objetos deberán contener una serie de valores que condicionarán el resultado del proceso a ejecutar sobre ellos, por lo que en nuestros tests unitarios deberemos crear instancias adecuadas, que formarán parte de lo que se conoce como test fixture.
Siempre que me he encontrado con este escenario he tenido que comerme la cabeza junto con mi equipo sobre cuál es la mejor forma de hacer legibles los contenidos de estas instancias de dominio. Como base para el resto del artículo vamos a definir una clase Person
con esta estructrura:
Nada complicado, en general es normal encontrarse con estructuras de datos más complejas.
Digamos que tenemos que testear una clase PersonProcessor
, que expone en su API el siguiente método:
El proceso ejecutado no viene al caso, solo importa saber que según los contenidos de la instancia Person
, la salida será una u otra. Según he comentado anteriormente, nuestro código de producción nunca generará instancias de persona directamente, que serán pobladas automáticamente por el framework de acceso a datos que utilicemos. Pero en un test unitario somos nosotros quienes deberemos hacer esto, es decir, buscamos algo así:
Veamos cuál sería la solución más directa, que utiliaría únicamente los métodos expuestos por Person
:
No sé a vosotros, pero a mí personalmente este tipo de código siempre me ha parecido muy poco directo y nada claro de leer rápidamente para hacerse una idea de los contenidos del objeto de dominio instanciado.
Alternativas a la instanciación directa
Existen varias alternativas para crear estos objetos de forma más clara, pero la mayoría de ellas requieren añadir librerías adicionales a nuestros tests (cosa que, por otro lado, tampoco es el fin del mundo). Echemos un vistazo rápido a estas soluciones.
Spock
Mi alternativa favorita es utilizar el framework Spock, del que ya hablamos en el blog. Gracias a esto, y a poder utilizar las facilidades del lenguaje Groovy, instanciar objetos complejos es muy sencillo utilizando el “map constructor” que expone Groovy para las clases de forma automática:
Personalmente encuentro esta solución muy limpia y adecuada, pero no todo el mundo es partidario de utilizar Spock y Groovy en sus tests
Serializar en ficheros de texto
Podemos almacenar el contenido del objeto en un fichero en formato JSON o YAML, y deserializar estos ficheros en nuestros tests utilizando librerías como GSon o YAML Beans. Echad un vistazo a los links para comprobar como sería el proceso (bastante sencillo por otro lado). El punto negativo de esta solución es que nos obliga a navegar entre ficheros de texto y nuestro código de test para entender qué está ocurriendo realmente.
Crear factorías de test
Por último, y si queremos evitar añadir dependencias o frameworks adicionales para una tarea que debería ser bastante sencilla, podemos crear factorías de test que nos asistan en la creación de nuestros objetos de dominio.
La primera alternativa sería encapsular el código visto en el primer ejemplo de todos, de esta forma:
Por lo que en nuestros test llamaríamos al método factoría directamente. De esta forma seguiríamos teniendo que navegar entre ficheros, pero es más directo que abrir ficheros de texto, etc. El problema es que seguiríamos teniendo la dificultad añadida de asimilar de manera rápida los contenidos de la instancia.
Como alternativa, una solución que comenzamos a utilizar recientemente en un proyecto es implementar una suerte de “factory helpers” que permiten la instanciación de manera anidada de forma bastante clara. Antes de ver el código de dicha factoría creo que es mejor comprobar de qué forma sería utilizado, aquí tenemos un ejemplo:
La estructura de Person
queda así expuesta de forma muy clara en los tests. El código de la clase factory puede llegar a resultar un pelín enrevesado, pero viene a compensar por su facilidad de uso, veamos la clase completa (también tenéis el código en GitHub):
Tenemos un conjunto de métodos factory, todos ellos con el nombre del campo que están creando (sin verbos tipo create
, build
, etc), y que utilizados de forma anidada nos llevan a crear instancias de manera muy legible. Quizás resulta algo chocante la existencia de métodos como name
que no son más que funciones identidad, pero repito, la idea de esta clase es facilitar le legibilidad de los tests, y creo que se consigue de sobra.
El mayor problema con esta solución lo tendríamos en estructuras muy complejas, con varios niveles de anidamiento, etc. En ese caso, seguramente sean mejores alguna de las otras soluciones, pero en general, creo que conviene conocer todas las opciones a nuestro alcance antes de decidir la que mejor se ajuste a nuestras necesidades.
¡Si conocéis alguna otra alternativa no dudéis en dejar un comentario!