Notas sobre Growing Object-Oriented Software, Guided By Tests – Parte 4: Escuchando a los tests.

En esta cuarta parte del libro Nat y Steve nos cuentan que el código de los tests debe ser cuidado tanto como el código de producción. A lo largo de este post voy a resumir solo la parte de «Escuchando a los tests» ya que creo que se merece dedicarle un post entero.

Normalmente, cuando encontramos una nueva funcionalidad que es difícil de testear existe una razón detrás de ello. Si aprendemos a escuchar a los tests conseguiremos simplificarlos y a su vez mejorar el diseño de nuestra aplicación.

Sigue leyendo «Notas sobre Growing Object-Oriented Software, Guided By Tests – Parte 4: Escuchando a los tests.»

Notas sobre Growing Object-Oriented Software, Guided By Tests – Parte 3: Desarrollando un ejemplo.

En la tercera parte del GOOS los amigos Nat Pryce y Steve Freeman nos cuentan como construyen una aplicación que permite automáticamente pujar online en subastas. Ellos empiezan a escribir la aplicación partiendo desde la iteración 0, mediante pequeños incrementos de funcionalidad y TDD van dando forma al software que quieren conseguir.

En este post voy a citar algunos puntos y consejos que ellos destacan durante el proceso de la construcción de la aplicación. Para profundizar en el proceso es imprescindible leer todos los capítulos que comprenden esta parte.

Divide y vencerás.

Es importante separar en pequeñas porciones las funcionalidades que se quieren implementar e ir añadiendo esas porciones incrementalmente y solo una cada vez. A la hora de refactorizar se debería seguir el mismo principio, pequeños refactorings que no comprometan el estado de la aplicación.

Iteración 0.

La «iteración 0» sirve para crear el esqueleto de la aplicación junto con el primer test de aceptación. Esto ayuda a entender desde el principio la automatización de la construcción, empaquetado y despliegue de la aplicación. Puede suponer un importante esfuerzo al principio pero es una de las tareas más importantes ya que si se deja para más tarde podrían surgir problemas inesperados.

Tests de aceptación.

Cada vez que se implemente una nueva porción de funcionalidad se debería añadir un test de aceptación simulando un evento de entrada a la aplicación para lograr un efecto en ella. Una característica de los test de aceptación es que permiten explorar el espacio que hay en medio desde que el mensaje entra a la aplicación y el resultado que produce ese mensaje.

Código mantenible.

Los equipos deberían invertir tiempo en escribir código mantenible a la vez que van añadiendo nuevas funcionalidades, ya que si no se hace, el coste de mantenimiento a largo plazo será cada vez mayor. Algunos consejos a tener en cuenta:

  • Principio de responsabilidad única, ayuda a separar la complejidad.
  • Métodos pequeños para expresar intención.
  • No tener miedo de crear nuevos tipos, por ejemplo, encapsular tipos primitivos ayuda a expresar el dominio de una mejor forma.
  • Encapsular colecciones. Utilizando el lenguaje del problema en lugar del lenguaje del lenguaje de programación ayuda a describir mejor un concepto de dominio.

Tests.

Algunos consejos útiles que los autores recomiendan a la hora de escribir tests:

  • Utilizar null cuando el argumento en el test no importe.
    Chat UNUSED_CHAT = null;
  • A veces puede ser beneficioso calcular el valor esperado en un test ya que hace el test más legible. Algunos puntos negativos de esta técnica pueden ser que haya que escribir el mismo código en el test que en la implementación y el cálculo puede ser demasiado complicado.
  • Pair programming es beneficioso durante todo el proceso de desarrollo, puede serlo incluso más durante la fase de refactoring en el ciclo de TDD, ya que aporta varios puntos de vista a la hora de realizar el diseño.

Decisiones.

Decidir si realizar o postponer un refactoring o el cambio de diseño de la aplicación es una difícil decisión. Es fundamental tener en cuenta los trade offs que hay detrás de ese cambio y no hay una regla común para decidirlo, requiere experiencia y madurez técnica.

No tener miedo a cambiar el nombre de los objetos es una parte esencial del proceso de desarrollo de software. Cada vez que conocemos más la aplicación y el dominio irán surgiendo mejores nombres y abstracciones las cuales facilitarán la lectura del código.

 

 

Notas sobre Growing Object-Oriented Software, Guided By Tests – Parte 1: Introducción

Notas sobre Growing Object-Oriented Software, Guided By Tests – Parte 2: TDD.

 

Notas sobre Growing Object-Oriented Software, Guided By Tests – Parte 2: TDD.

En este segundo capítulo del GOOS, Nat Pryce y Steve Freeman nos explican lo que puede aportar el ciclo de TDD y cómo sería para ellos un buen diseño orientado a objetos.

TDD.

Incertidumbre y fuente de feedback.

Los riesgos de dejar el despliegue de la aplicación para el final son demasiado altos. Nada mejor que automatizar el despliegue desde el principio para entender el proceso y que el equipo entienda como encaja.

Tener un sistema desplegable es útil para poder lanzar los tests contra él. Esto aportará fuente de feedback en el proceso de desarrollo, sobre la calidad del sistema y del código.

Todo este trabajo previo de automatización puede parecer excesivo pero ayuda a exponer la incertidumbre lo más pronto posible.

Mantener el ciclo de TDD.

Empezar escribiendo un test de aceptación en el lenguaje del negocio/dominio ayuda a entender lo que el sistema debe de hacer, sin hacer presunciones de como tiene que ser la implementación.

Los tests unitarios nos ayudan a diseñar los objetos necesarios para cumplir con el objetivo.

Una de las mejores cosas que se puede conseguir con TDD es la confianza de cambiar el código sin miedo a romperlo. El miedo paraliza el progreso.

Consejos:

  • Es preferible empezar escribiendo los tests con el caso más sencillo posible e ir añadiendo tests para validar nuestras ideas e ir haciendo más robusta la nueva feature.
  • Deberíamos escribir el test que nos gustaría leer, sin tener en cuenta si el código compila, centrándonos solo en cómo está escrito.
  • Es fundamental ver fallar el test para comprobar si falla de la forma que esperamos.
  • Los tests unitarios deberían comprobar el comportamiento de una unidad, no todos los posibles caminos del código.
  • Escuchar a los tests. Cuando el código es difícil de testear, probablemente el diseño de la aplicación necesita ser mejorado.

Orientación a objetos.

Para diseñar una aplicación mantenible hay que centrarse principalmente en dos objetivos. El primero es la separación de responsabilidades que ayuda a cambiar menos código cuando hay que implementar una nueva funcionalidad. El segundo es elevar el nivel de abstracción, ya que se consigue bajar la complejidad.

Consejos:

  • Exponer solo lo necesario en la interfaz pública de nuestros objetos.
  • Encapsular el comportamiento de un objeto para que pueda solo ser modificado a través de su API.
  • Ocultar como un objeto implementa la funcionalidad para la que existe.
  • Un objeto solo debe tener una responsabilidad.
  • Nombrar objetos sin utilizar ningún tipo de conjunción («y», «o», «con»).
  • Construir un objeto válido desde el constructor.

Relaciones entre los objetos.

Se puede categorizar la relación entre los objetos en tres tipos. Un objeto puede tener  relaciones con objetos que son dependencias, objetos a los que notificar y objetos que ajustan el propio comportamiento del objeto.

Al componer un objeto con otros objetos se espera que la API del nuevo objeto oculte los componentes del nuevo objeto y sus interacciones, exponiendo una abstracción más simple.

Logrando un diseño orientado a objetos.

Empezar primero escribiendo los tests ayuda con el diseño y a describir lo que se quiere conseguir y no cómo conseguirlo. Para construir el objeto que se quiere testear es necesario conocer las dependencias del objeto, esto ayuda a mantener la independencia del objeto sobre la aplicación y a conocer como se relaciona el objeto en el sistema.

Value types.

Los conceptos de dominio deberían de estar definidos como tipos aunque no tengan demasiada implementación.

Tres técnicas básicas para introducir value types son:

  • Extraer nuevos tipos que ayuden a eliminar la complejidad de un objeto que tiene múltiples responsabilidades.
  • Introducir un nuevo concepto de dominio.
  • Agrupar grupos de valores que son usados siempre conjuntamente.

Identificar relaciones con interfaces.

Usar interfaces para identificar los roles que un objeto cumple en el sistema ayuda a describir los mensajes que el objeto acepta.

Cuantos menos métodos tenga la interfaz más claro será el rol que define, además será más fácil escribir adaptadores y decoradores para ella.

Clases.

Son un detalle de implementación, una forma de implementar los tipos.

Construir sobre código de terceros.

Uno de los principales problemas cuando se utiliza código de terceros es que no tenemos el control sobre él y no podemos usar nuestro proceso para guiar el diseño. Es aconsejable centrarse en el proceso que integra nuestro diseño y el código de terceros.

Consejos:

  • Mockear tipos que te pertenecen.
  • Escribir adaptadores para comunicarse con el código de terceros.

Enlaces relacionados:

Notas sobre Growing Object-Oriented Software, Guided By Tests – Parte 1: Introducción

 

Notas sobre Growing Object-Oriented Software, Guided By Tests – Parte 1: Introducción

Empecé a leer el GOOS hace unos días y me di cuenta que para profundizar en el libro tenía que hacer algo más que leerlo. Por eso voy a empezar a escribir en el blog algunos posts  con notas del libro que me parezcan interesantes. Por supuesto solo serán unas notas, para profundizar con el libro y sacarle todo el jugo posible habrá que leerlo y releerlo varias veces.

Parte 1: Introducción

Es necesario contar con procesos / herramientas que proporcionen feedback lo más rápido posible para solucionar la incertidumbre que existe cuando se empieza a desarrollar un proyecto de software.

El desarrollo iterativo refina progresivamente la implementación de las nuevas funcionalidades hasta que son suficientemente buenas.

Sigue leyendo «Notas sobre Growing Object-Oriented Software, Guided By Tests – Parte 1: Introducción»