31.3.05

Una pregunta en el aire

Saludos.

El otro día, leyendo una página web sobre una aplicación encontré lo siguiente:

Robusto: Más de 230 pruebas Junit.

La primera pregunta que se plantea es bastante obvia: ¿Podemos medir el grado de robustez o fiabilidad de un sistema mediante el número de pruebas?.

No. Un ejemplo muy claro, aunque tal vez un poco extremo. Imaginemos el
siguiente código (que sirve tanto para C como para Java).


int factorial(int n) {
if (n == 1)
return n;
return n * factorial(n-1);
}


Supongamos que escribo 100 pruebas para comprobar los 100 primeros factoriales. ¿Puedo asegurar que el código anterior es robusto?. No. en cuanto reciba un 0 o un número negativo fallará a pesar de sus 100 (o 11000) pruebas.

Otra pregunta, tal vez no tan obvia, es si la robustez o fiabilidad de un sistema está garantizada únicamente por pruebas unitarias.

No. Un ejemplo muy claro lo encontramos en las aplicaciones web. Con pruebas
tipo JUnit, u otra herramienta similar, difícilmente podremos comprobar la respuesta del sistema a un gran número de peticiones concurrentes de distintos clientes, ni si el sistema es capaz de seguir funcionando o se colapsa.

La gran pregunta que habría que responder es: ¿Como podemos garantizar la robustez del sistema?. Lo cual nos lleva a plantearnos: ¿y qué es la robustez?.

Habrá que seguir con esto.

24.3.05

Probando exhaustivamente

El siguiente ejemplo está sacado de un libro sobre pruebas bastante famoso.
Nos piden un programa para clasificar un triángulo en equilátero, isósceles o escaleno. Para que tres lados a, b y c formen un triángulo debe cumplirse que s (s = (a +b +c) / 2) sea mayor estricto que a, b y c.
Una posible solución se muestra a continuación.



static final int NOTRIANGULO = 0;
static final int EQUILATERO = 0;
static final int ISOSCELES = 0;
static final int ESCALENO = 0;

public static int tipoTriangulo(int a, int b, int c) {
int s;

s = (a+b+c) / 2;
if ( (s <= a) || (s <= b) || (s <= c))
return NOTRIANGULO;

if ( (a==b) && (b==c) )
return EQUILATERO;

if ( (a==b) || (b==c) || (a==c) )
return ISOSCELES;

return ESCALENO;
}



Sin embargo, lo más interesante es plantear que pruebas necesitamos para verificar a fondo que el código anterior funciona. En este caso, una prueba va a ser un conjunto de valores concretos y el resultado esperado, por ejemplo:

(3, 3, 3) : EQUILATERO
(3, 3, 4) : ISOSCELES
(3, 4, 5) : ESCALENO
(2, 2, 6) : NOTRIANGULO

¿Cuantas pruebas necesitamos para tener la certeza que hemos probado todas las posibles combinaciones?. ¿Cómo obtenemos esas pruebas?. Una técnica clásica que podemos utilizar es analizar las estructuras de control. Esta es una técnica de caja blanca.
Por ejemplo, en el caso de un triángulo isósceles, vemos que ha de cumplirse una de tres condiciones posibles. Por tanto, en este caso hemos de verificar las tres condiciones. Las pruebas resultantes se muestran a continuación:

(3, 3, 4), (3, 4, 3), (4, 3, 3) : ISOSCELES

Aplicando este mismo proceso al resto de los casos obtenemos el resto de pruebas. Sin embargo esto aún no es suficiente. Por ejemplo: ¿qué sucede cuando un lado es 0 o negativo, o cuando un lado vale el valor máximo que un tipo int puede almacenar?. ¿Es posible introducir lados decimales o lados que no sean valores numéricos?. Todas estas circunstancias deberían ser probadas también.
En este caso, para completar el conjunto de pruebas, podemos aplicar una técnica de prueba de caja negra llamada análisis de valores límites. Por ejemplo, para verificar que el programa sigue funcionando cuando uno de sus lados es 0 las pruebas serán:

(0, 3, 3), (3, 0, 3), (3, 3, 0) : NOTRIANGULO

Calculando todas las posibles combinaciones se obtiene aproximadamente 30 casos de prueba distintos, lo cual da una medida de lo importante que es probar convenientemente cualquier código.

22.3.05

Enlaces sobre refactorización

Saludos.

Para aquellos interesados en tener a mano información breve y funcional sobre refactorizaciones, puede echarle un vistazo a estos dos enlaces publicados en Agile Spain (www.agile-spain.com/):

En el primero nos muestran una tabla con malos olores y enlaces a las refactorizaciones recomendadas para eliminarlas:
http://wiki.java.net/bin/view/People/SmellsToRefactorings

En el segundo nos explican las refactorizaciones mediante sencillos (muy sencillos) diagramas parecidoa a UML:
http://www.refactoring.be/thumbnails.html

Además de estos enlaces no hay que dejar de visitar el blog en español:
http://www.programacion.com/blogs/14_refactoring

15.3.05

Pruebas con aspectos

Saludos.

Nos han publicado en JavaHispano un trabajo que presentamos el año pasado en un estupendo workshop. El trabajo versa sobre como aplicar programación orientada a aspectos para escribir pruebas unitarias, y que ventajas aporta frente a la clásica herramienta JUnit. Los pocos ejemplos están escritos en Java y AspectJ.

Para quien tenga interés, el enlace es: http://www.javahispano.org/articles.article.action?id=96

12.3.05

Fin de la operación limpieza.

Saludos.

Aunque aún quedan algunos ejercicios con semáforos, la operación limpieza queda oficalmente clausurada. Si alguien quiere los ejercicios que faltan puede pedírmelos por correo electrónico.
Dentro de unos pocos meses empezaré otra operación de limpieza, pero esta vez para ejercicios básicos en Java.
Mientras tanto retomaré la idea original de este blog e iré escribiendo comentarios referentes a Java y reflexiones pruebas del software.