Cómo enseñamos TDD

TDD es una práctica cuyo punto clave es la secuencia de pasos que se siguen para obtener la solución. En Algo3 explicamos la teoría y luego la ponemos en práctica haciendo dojos en las clases. También les damos ejercicios a los alumnos y les pedimos que los resuelven haciendo TDD, pero la realidad es no tenemos forma de asegurarnos que hayan arribado a la solución usando TDD. Hay algunas situaciones observables que pueden sugerir que la solución no fue generada utilizando TDD (por ejemplo si la solución tiene baja cobertura es muy posible que no se haya utilizado TDD o al menos no se lo haya utilizado correctamente o todo el tiempo). Al mismo tiempo todos los ejercicios de programación que resolvemos en clase procuramos hacerlo usando TDD. Finalmente evaluamos su conocimiento sobre TDD con alguna pregunta en el examen. Creo que es un enfoque bastante integral y razonable para el contexto de la materia, donde es común que tengamos más de 40 alumnos por curso. Pero es posible que haya mejores enfoque para otros contextos.

Sin ir más a lejos, yo mismo en UNQ estrené este cuatrimestre un enfoque distinto. Cabe aclarar que el contexto de UNQ es distinto, en primer lugar es una materia de ingeniería de software donde se supone que los alumnos ya vieron algo de TDD. Al mismo tiempo, la cantidad de alumnos es mucho menor, suelen ser alrededor de 10. Finalmente la dinámica de la materia es distinta: no tenemos separación explícita entre teoría y práctica y tampoco tenemos exámenes formales. Lo que hacemos (o mejor dicho lo que hemos hecho este cuatrimestre) es explicar TDD haciendo TDD, de una, explicando la teoría sobre la misma práctica. Luego les damos a los alumnos algunas katas para resolver y les pedimos que graben un screencast mientras van resolviendo la kata. Esto nos permite observar cómo los alumnos aplican la técnica paso a paso y detectar si algo no fue correctamente entendido.

Implementación de Step Definitions en Cucumber-JVM

Continuando con los dilemas del uso de cucumber, luego de un par de reuniones con los analistas/testers del proyecto, tomamos algunas decisiones:

  • Escribir los steps con las menor cantidad de parámetros posibles
  • Agrupar los step definitions en base a conceptos de negocio
  • Utilitzar Step definitions con estado (stateful)

A partir de esto, el flujo de trabajo es: los analistas/testers identifican los casos de tests y los expresan como escenarios usando lenguaje Gherkin. Luego usando las anotaciones de Cucumber-JVM yo genero los métodos que implementan los pasos de los escenarios. Estos métodos son los que se conocen como “Step definitions”. Hasta aquí llega la herramienta. La forma en que se implementan los Step Definitions depende de cómo sea la aplicación que se pretende testear. Si quisiéramos testear una aplicación web, posiblemente usariamos el driver de Selenium. En mi caso, tengo que testear un sistema de facturación a través de una interfaz propietaria basada en mensajes XML. Más allá de la interfaz hay dos cuestiones que requieren interactuar directamente con la base de datos del sistema:

  1. Limpiar los datos luego de la ejecución de cada test
  2. Realizar ciertas verificaciones (asserts) sobre los datos generados por el sistema que no se encuentran disponibles en la interfaz

Para lidiar con todas estas cuestiones he generado un conjunto de clases (¿micro-framework?) que agrupan código común y que me permiten elevar un poco el nivel de abstracción. cucumber_domain   Scenario, no es una clase, es un caso de prueba especificado en lenguaje Gherkin en un archivo de extensión .feature (un feature suele contener varios scenarios). StepsDefinition no es una clase, sino que son métodos agrupados en clases según criterios de negocio. BusinessAction, son clases que agrupan operaciones de alto nivel con granularidad de negocio. ScenarioContext, es un singleton donde se almacena el estado a lo largo de la ejecución de los distintos steps que conforman un scenario. Este contexto es reseteado cada vez que se ejecuta un scenario. Driver, es una clase con operaciones “de bajo nivel” que permite la interacción con el sistema que se está testeando. DBHelper, es una clase con operaciones para interactuar con la base de datos, se utiliza para resetear el estado del sistema y también para realizar algunas verificaciones que no pueden hacer utilizando el driver/api. Los siguientes diagramas muestran la interacción de estos componentes. cucumber_sequence1     cucumber_sequence2

La polémica: TDD está muerto

Recientemente David Heinemeier Hansson, creador del framework Rails, publicó un artículo titulado TDD is dead, long live testing que causó cierto debate con referentes de la disciplina. Incluso Uncle Bob y Kent Beck dedicaron incluso algunas líneas a la cuestión.

Personalmente creo que TDD es una práctica muy útil, la uso a menudo, pero no todo el tiempo ni para todo. Me gusta y suelo usar  TDD cuando tengo que escribir lógica negocio. Generalmente no uso TDD cuando tengo que escribir lógica de presentación y cosas por el estilo.

Lo que destaco de este intercambio de opiniones es que me ha dado material para trabajar con mis alumnos :-).