enero 26, 2011

Testeo defensivo, o que disparen a ese API

A raíz de una entrada de @sergioazo y su correspondiente conversación, he recordado una técnica que leí en algún sitio para minimizar los problemas en el trabajo con librerías externas de terceros en tu proyecto.
Se puede usar para varias cuestiones:
  • Comprender el funcionamiento real de una librería (no el esperado)
  • Defenderte frente a cambios en el comportamiento futuro de la librería
  • Documentar el uso de una librería, su API.
Se puede decir que vamos a generar test unitarios, en los que no podemos escribir el nombre del método de test hasta el final. Si hacemos TDD, el nombre del test deberá indicar que comportamiento queremos asegurar, y después programamos ese test. En este caso, podremos poner un nombre al método realmente coherente al test, cuando ya tenemos los assert ejecutando y comprobados en verde. Quizás coincida con lo esperado, o quizás no. Sugiero que se guarden los dos nombres, para comprender las diferencias encontradas, que probablemente también desconcierten al próximo programador que coja tu código que usa esa librería.

Si tenemos tests de los usos que hace nuestra aplicación de una librería, el cambio de versiones de la misma, puede ser testeado. Basta con ejecutar nuestra suite de integración, para que nos corrobore si la librería sigue funcionando tal y como esperamos.

Veamos un ejemplo. Imaginemos que tenemos un API tal que así:

public String guardaDocumento(String docType, byte[] data);
public byte[] obtenerDocumento(String docCode);

Y no tenemos más información, parece bastante obvio lo que hace, pero queremos asegurarnos, y sobre todo tener cuidados con los cambios con esa librería que se está desarrollando. Podemos hacer un test de la siguiente manera:

public void deberiaDevolverElMismoDocumentoPorCodigo(){
Repositorio repositorio = new Repositorio();
String docCode = repositorio.guardaDocumento("tipo1", "Hola Mundo".getBytes());
assertArrayEquals("Hola Mundo".getBytes(),
repositorio.obtenerDocumento(docCode));

}
Pero, este test, tan simple como almacenar un documento y obtenerlo de nuevo, se queda en rojo. ¿por qué? Por que resulta que el código necesita después, no es el mismo código que devuelve
Por lo que finalmente tras unas pruebas (o un telefonazo a alguien harto de tus preguntas) llegamos a la conclusión, que el código que necesita el "obtenerDocumento" es la concatenación del tipo de documento y el código devuelto al guardarlo.
Por tanto, modificamos el test, y después modificamos el nombre del test.

/**
* was:deberiaDevolverElMismoDocumentoPorCodigo
*/
@Test
public void deberiaDevolverElMismoDocumentoPorTipoDocumentalYCodigo(){
Repositorio repositorio = new Repositorio();
String docCode = repositorio.guardaDocumento("tipo1", "Hola Mundo".getBytes());
assertArrayEquals("Hola Mundo".getBytes(),
repositorio.obtenerDocumento("tipo1" + docCode));
}
¿conseguimos con esto los objetivos inicialmente planteados?

(nota:basado en hechos reales)