¿Como vuelvo testeable mi código? Algo huele mal.

Veamos el siguiente código:

image

Lo que buscamos crear al menos una prueba unitaria para este. ¿Cómo lo hacemos?. Bueno, antes quiero plantear algunos supuestos:

  • Si bien parece un TransactionScript, hagamos de cuenta que no lo es. El hecho de tener solo un método y ningún campo, propiedad o evento es solo para hacer de este un ejemplo ultra sencillo. Por lo tanto pensemos que sí tiene campos, propiedades y varios métodos.
  • Si bien usamos un System.Net.Mail.SmtpClient, la idea es no limitarse solo a esta clase sino que puede ser cualquier otra (en este caso una clase de la BCL). Quizás no tenemos el código para modificarlo (como en este caso) o quizás sí lo tenemos pero modificarlo no es buena idea.
  • En principio solo queremos verificar que la transferencia se realiza y NO que el mail se envía correctamente.

Las opciones que analizaremos aquí son:

  • Inyección de dependencias (DI)
  • Sub clasificación
  • Decoración

Inyección de dependencias

Usar un Gateway es uno de los caminos más habituales que podemos encontrar para “volver testeable nuestro código”. Hacer esto con el único objetivo de testear el código para mí no solo que no vale la pena sino que es incorrecto. Es una sobre ingeniería, a pequeña escala en este caso, y un despropósito puesto que difícilmente tendremos en el futuro otro tipo de cliente de mail. El único cliente de mail extra existe solo con motivo de testing. Y además tenemos 2 constructores, una interface y una clase extra, etc, etc… horrible. ¡Si tan solo el método Send del SmtpClient fuese virtual!

image

Si no quisiéramos pagar ese costo, ahorrarnos la interface y la clase extra pero seguir utilizando la DI como mecanismo, lo que podríamos hacer es darnos una vuelta por el mundo dinámico. Básicamente, cualquier tipo que responda a “Send(string, string, string, string)” nos va a servir.

image

Las dos principales objeciones a esta alternativa vienen de parte de aquellos a quienes les disgustan (¿o asustan?) las implicaciones del chequeo de tipos en tiempo de ejecución como de aquellos a quienes les disgusta el inyectar este tipo de dependencias. Por lo demás, esto facilita bastante la creación de las pruebas. Abajo hay dos pruebas que usan tipos anónimos para realizar dos tipos de pruebas distintas.

image

Una solución intermedia entre inyectar tipos estáticos y dinámicos es inyectar acciones. Es decir, inyectar la pieza de código que queremos que se ejecute para enviar los mails como vemos abajo. Esto también facilita enormemente la creación de pruebas y si bien es aceptable, tiene, además del mismo mal olor de todas las alternativas que requieren inyectar dependencias, el problema de ser poco intuitiva. Veamos un ejemplo:

image

Conclusión parcial: la inyección de dependencias, en cualquiera de sus formas, nos puede ayudar a crear código más testeable aunque requiere de cierto código extra y ciertos patrones que no siempre redundan en mejor código. Por esta razón, la DI, en casos similares a los de los ejemplos que he presentado, no es una buena alternativa.

Sub clasificación

Uno podría argumentar que quizás nuestro método Tranfer necesita una pequeña refactorización la cual nos podría ayudar a mejorar el código y de pasada, hacerlo más testeable… veamos:

image

Ahora podemos crear versiones testeables de esta clase, ya sea a mano o utilizando algún framework de aislamiento.

image

Esto parece algo más limpio ¿verdad? No necesitamos extrañas interfaces, constructores, tipos dinámicos ni nada de lo que vimos con anterioridad. Una objeción: ¿qué sucede si la clase necesita ser sealed? Este punto de extensión ha sido creado nuevamente con el único propósito de posibilitar las pruebas porque de lo contrario, el método no sería protected virtual sino simplemente private. Ok, pero es una alternativa válida y nos sirve.

Decoración

Quizás la responsabilidad de enviar las notificaciones no sea algo propio de nuestra clase TranferManager y por lo tanto podríamos sacar esa responsabilidad a otra clase usando el patrón decorador (o podríamos usar un wrapper también). Veamos:

image

Ahora podemos testear TranferManager sin dificultades. El problema que se nos presenta ahora es que tal vez lo que necesitamos testear ahora sea la nueva clase TranferManagerWithNotification.

Esta entrada se ha vuelto muy extensa así que la continuaré más adelante…

Sin categoría

2 thoughts on “¿Como vuelvo testeable mi código? Algo huele mal.

  1. Seamos serios. Al realizar un «override» sobre el método «SendTransferNotification» no pruebas el funcionamiento de su implementación, sólo compruebas cosillas muy superfluas como el parámetro que le pasas.

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *