sábado, 22 de agosto de 2009

Como hacer el unit testing mas fácil

Cuando se habla de tdd, exiten muchas herramientas y técnicas asociadas al testing unitario. Mi compañero Sergio hizo un muy resumen de ellas en su blog:

Además de estas técnicas, creo que hay algo fundamental para hacer una aplicación fáclmente testeable y tiene que ver con el diseño. A continuación voy a enumerar algunos aspectos que me parecen importantes.

Modelo de dominio

Resolver la lógica de la aplicación usando un modelo de dominio (o domain model pattern). Testear lógica desde un modelo de dominio es relativamente simple, solo hay que crear objetos, asignar propiedades, llamar a métodos y hacer asserts. Esto se debe a que un modelo de dominio no tiene depentencia a ninguna cuestión de infraestructura como persistencia, seguridad, etc. Esta separación natural hace que las pruebas sean más unitarias.

Me ha pasado de trabajar en aplicaciones donde había que modelar las entidades del dominio sin métodos. Esto es conocido como un antipatrón llamado anemic domain model. Construir pruebas unitarias en este caso es una tarea dura, pues generalmente otro objeto encapsula la lógica. En mi caso, las pruebas dependian de los datos cagados en la base de datos, cuando estos cambiaban, las pruebas dejaban de andar. También pasaba que había casos donde el test modificaba los datos y al hacer esto, invalidaba otras pruebas. Estos esfuerzos adicionales dificultan las construcción y el uso de pruebas unitarias. También pasa que con el tiempo las pruebas dejan de ser válidas ya que nadie mantiene los datos en la base. Según mi experiencia, hubiese sido muy bueno en estos casos trabajar con un modelo de dominio.

Para crear buenos modelos de dominio, recomiendo el libro domain driven design.

Programar hacia interfaces, no implementaciones

Este es uno de los principios de diseño que más me gusta. Si cada objeto de una aplicación (excluyendo al model de dominio) fuera una estrategia intercambiable, sería muy fácil aislarlo para poder testearlo unitariamente. Esto nos lleva al siguiente punto.

Inyección de depencias

Un framework de inyección de dependencias facilita la creación de pruebas unitarias (y de la aplicación). Algo que hago es crear un contexto especial para los casos de test que me permite modificar las dependencias según más convenga. Por ejemplo un servicio (service layer) que usa un objeto para la autorización puedo asociarlo a uno que siempre autoriza para probar determinada funcionalidad. Esto se conoce como mock objects.

Algunos trucos para construir pruebas unitarias usando spring framework
  • Crear una clase TestBase que herede de AbstractJUnit4SpringContextTests. Esta clase define el application context de test a usar.
  • Usar notaciones para definir las dependencias de un test

    Para insertar depencias por tipo:
    @Autowired
    private CatalogService catalogService

    Para insertar dependencias por id del bean
    @Resource
    private Client clientMario

  • Si la persistencia se realizar en base de datos, usar AbstractTransactionalJUnit4SpringContextTests. Esta clase realiza automáticamente un rollback de las operaciones realizadas. Yo la uso desde una clase test base:
    http://www.codepaste.net/4poaxh

  • Marcar los casos de test que ensucian los beans cargados por spring. Esto ayuda a eliminar las dependencias entre test. Por ejemplo si en un test se agregan items al cache y en otro test es necesario que el cache esté vacío para no afectar la prueba, se puede marcar el 1er caso de test como @Dirty y spring volverá a cargar todos los beans nuevamente luego de ejecutar la prueba.

    @Test
    @Dirty
    public void addItemsToCache() {...

  • Definir beans de las entidades del modelo con los resultados esperados en el contexto de test. Esto es algo que evita el hardcodeo de los resultados esperados. Luego se le agrega al caso de test una dependecia a este objeto.

    @Resource
    private Client expectedClient
Espero sea de ayuda. Para mi hay un antes y un después de usar spring.

2 comentarios:

Sergio Salanitri dijo...

Muy interesante Fernando, ¿es posible aplicar estos trucos usando el framework Spring.Net?
Para el caso de mocks objets está el framework JMock o EasyMock, lo estuve viendo el NMock para .Net y resulta bastante últil.
Para DDD está el libro DDD Quickly que lo podés bajar gratis de
http://www.infoq.com/minibooks/domain-driven-design-quickly

Fernando Claverino dijo...

Hola Sergio. Nunca trabajé con spring.net, pero no creo que estos muchachos hayan excluido esta funcionalidad. Saludos!