jueves, 14 de febrero de 2013

TDD mental en ambientes hostiles

La vida me ha llevado a ambientes extremadamente hostiles, donde no se puede instalar nada, ni sacar luego nada. El que no pueda instalar o desarrollar un framework de Unit Testing en una máquina no significa que no pueda tenerlo en la mente, desplegarlo en el momento y descartarlo al final.
El asunto a resolver en esta ocasión es tomar una lista de usuarios y detectar los potenciales repetidos. Lo normal en unix es sort | uniq –d y en mainframe un SORT … OUTPUT PROCEDURE … en COBOL.
El problemas es que pueden haber nombres distintos que corresponden a una misma persona debido a la evolución histórica del sistema, por ejemplo “Lopez, Juan” y “Lopez_Juan” son buenos candidatos a ser la misma persona.
Antes de ordenar y buscar repeticiones, necesito una función de normalización de nombres, que lleve todos los nombres a la forma “Apellido(s), Nombres(s)”.
Para nuestro primer test, empezamos como siempre por el caso positivo.






Y una implementación sencilla que debería pasar el test:







Acá podemos ver como se ejecuta con ISPF:



Y su resultado:



El siguiente paso es un caso que falle:



Volvemos a ejecutar y vemos el error en el output



Y el valor de retorno RC=1, que era RC=0 en el caso correcto.



Esta es la nueva implementación para que funcione:



Ahí me asaltó una duda, pues space() dice que “remove all superfluous white space, including white space between words. “, así que agregué un test para más espacios y puse doble nombre, en dos pasos separados, obvio.



Nos encontramos con que el if expected… end se torna monótono, llegó el momento de refactorizar el test



Ya se parece más a lo que estamos acostumbrados


Para variar, he hecho trampa, pues ya había implementado tanto el código como los test, pero en COBOL. Sin embargo no te he engañado, pues seguí unos pasos similares y para la implementación en REXX recién en este punto pasé a copiar los casos que ya había utilizado en la versión en COBOL.

Este es el test:



Este es el código a testear



Tras un rato de idas y vueltas, ya tenemos un set aceptable




Y la implementación que pasa



Como se puede apreciar comparando los test en uno y otro lenguaje, en el caso de COBOL he optado por hacer algo parecido a un data provider pero no encapsulé la comparación como con el ASSERT de REXX. No vale la pena el data provider en REXX ya que es la misma cantidad de código y un poco más de complejidad. No vale la pena el ASSERT en COBOL, pues como es un lenguaje tipeado habría que hacer una versión para cada tipo de datos y como no hay overloading, tendría que usar nombres distintos y todo ese trabajo que asociamos con hacer un framework, que no era la idea.

De más está decir que no soy System Application Programmer, acepto críticas y sugerencias, pero no reproches.

Notas para entender un poco mejor el código y los conceptos presentados


ISPF


Es un programa que entre otras cosas permite editar datasets (archivos de mainframe) y en el caso de programas REXX ejecutar con “EX”.

REXX


Lenguaje interpretado con origen en mainframe pero extendido a múltiples arquitecturas y sistemas operativos.

Hay funciones y procedimientos, las funciones se llaman por su nombre y los procedimientos con CALL

Los argumentos tienen un sabor a perl, hay que sacarlos con PARSE, que tiene un sabor a erlang, ya que puede hacer pattern matching



En este caso, pone en ID todo lo que encuentre antes de “:”, luego en EXPECTED lo que haya entre “:” y “->” y por último en GOT el resto.

COBOL


Venerable lenguaje compilado con origen en mainframe y tambien extendido a múltiples arquitecturas y sistemas operativos.

¿Qué puedo decir en pocas lineas?

TDD


En una disciplina de programación/testing en la cual se identifica primero un requerimiento o error, se hace un test que lo expresa y que por supuesto falla, ya que aun no has implementado o corregido nada que lo haga pasar. Luego se implementa o corrige y se empieza otra vez.

La diferencia fundamental con el testeo natural que uno hace al programar es que los test se guardan y se siguen aplicando de modo acumulativo, lo cual da una cierta seguridad de que cualquier cambio que uno haga que rompa algo será detectado.

La diferencia fundamental con el testeo que se hace luego de la implementación, es que al testear al final uno se encuentra con desagradables sorpresas que pueden llevar a reestructuraciones drásticas. TDD da feedback inmediato y ayuda a comprender el problema. Además provee una suerte de documentación, ya que se muestra como se usa el código testeado.

Data Provider: cuando se hace el mismo test con distintos pares datos->expected, en lugar de hacer un test para cada dato, se hace uno solo y se lo alimenta con el conjunto de pares.

Refactorización: es una técnica que consiste en modificar la implementación sin modificar el comportamiento. Se suele usar para mejorar el código y va estrechamente ligado al testing, pues es éste quien avisa si ha cambiado el comportamiento.