Libertonia
Portada · Todo · Software Libre · Desarrolladores · Comunidad · Internet · Tecnología · Meta · Diarios
Unit test

ridiculum's Diary
Por ridiculum
departamento 1-2-3-probando-probando , Sección Diarios
Puesto a las Sun May 16th, 2004 at 06:01:55 PM CET
La peor parte del desarrollo de un programa es determinar que funciona correctamente. Ver que has escrito 1000 lineas y aquello no hace lo que debe es cabreante y es mas cabreante tirarte 3 dias para buscar un error estúpido.

 


Para evitar o al menos paliar estas cosas, hay una metodologia interesante que se llama test-driven development y que forma parte de la tan cacareada XP (eXtreme Programming).

Esta practica de TDD usa unas herramientas denominadas Unit Test. Unit Test hay para un buen puñado de lenguajes:

Para mi tfc estoy usando cpptest, principalmente por que tiene una caracteristica que lo diferencia del resto: permite comparar resultados en un intervalo. Esa caracteristica para mi es absolutamente fundamental, ya que todo mi tfc tiene valores en coma flotante y no puedo usar una comparacion de igualdad.

La verdad es que es bastante sencillo de usar. Veamos como se comprueba la clase Coordenadas.
Esta clase lo unico que hace es guardar la latitud y longitud de un punto y permite contruir la clase de 3 formas diferentes:

  • Teniendo la longitud y la latitud en decimal
  • Teniendo la longitud y la latitud en sesagesimal
  • Teniendo el par pais/ciudad que previamente existe en una hash

Veamos el ejemplo:

#include <iostream>
#include <stdexcept>
#include <coordenadas.h>
#include <cpptest.h>

class TestCoordenadas : public Test::Suite
{  
public:
   TestCoordenadas()
   {
      TEST_ADD(TestCoordenadas::constructor1);
      TEST_ADD(TestCoordenadas::constructor2);
      TEST_ADD(TestCoordenadas::constructor3);
      TEST_ADD(TestCoordenadas::constructor4);
   }  
private:
   void constructor1()
   {
      Coordenadas test1(34.32, 12.87);
      TEST_ASSERT_DELTA(test1.getLongitud(), 0.598997 ,1e-6);
      TEST_ASSERT_DELTA(test1.getLatitud(),  0.224624 ,1e-6);
   }
   void constructor2()
   {
      Coordenadas test2("Nigeria\\Yola");
      TEST_ASSERT_DELTA(test2.getLongitud(),0.217875,1e-6);
      TEST_ASSERT_DELTA(test2.getLatitud(),  0.16057,1e-6);
   }
   void constructor3() 
   {
      TEST_THROWS(Coordenadas(194.32, 12.87), std::domain_error);
   }
   void constructor4()
   {
      short pos1[4]={12,38,23,00};
      short pos2[4]={-12,59,32,00};

      Coordenadas test4(pos1, pos2);
      TEST_ASSERT_DELTA(test4.getLongitud(),0.220605 ,1e-6);
      TEST_ASSERT_DELTA(test4.getLatitud(), -0.226757,1e-6);
               
   }
};


int main()
{
   try
   {
      Test::Suite ts;
      ts.add(std::auto_ptr <Test::Suite> (new TestCoordenadas));

      Test::Output* output = 0;
      output = new Test::TextOutput(Test::TextOutput::Verbose);
      //Test::Output output( Test::TextOutput(Test::TextOutput::Verbose) );

      ts.run(*output, true);
   }
   catch(...)
   {
      std::cout << "excepcion no esperada\n";
   }

   return 0;
}

TestCoordenadas::contructor[1|2|4] comprueban que la construccion de la clase es correcta. Creamos una clase y comprobamos que el valor que devuelve getLongitud y getLatitud son correctos y establecemos como intervalo 1e-6. Hay que tener en cuenta que los resultados estan en radianes por que el resto del tfc esta lleno de operaciones de senos y cosenos con las coordenadas.
TestCoordenadas::contructor3 comprueba que la creacion de una coordenada que no existe falla y lazan la excepcion pertienente.

En el constructor de TestCoordendas registramos los test que queremos ejecutar y despues en el mail, incluimos TestCoordenadas en la clase Suite y luego ejecutamos la clase suite para que recorra todos las clases de test. La salida que tendriamos seria:

[raul@maxwell:~/ionoscom/test]./test
TestCoordenadas: 4/4, 100% correct in 0.000280 seconds
Total: 4 tests, 100% correct in 0.000280 seconds

y en el caso de que hubiera un error:

[raul@maxwell:~/ionoscom/test]./test
TestCoordenadas: 4/4, 75% correct in 0.000272 seconds
        Test:    constructor1
        Suite:   TestCoordenadas
        File:    test.cpp
        Line:    21
        Message: delta(test1.getLatitud(), 0.224624, 1e-6)

Total: 4 tests, 75% correct in 0.000272 seconds

La salida de cpptest es configurable y puede generar HTML texto, texto locuaz (para aquellos que dominan el ingles, verbose ;).

En codeproject, hay una serie de 5 articulos sobre unit test en C# bastate interesante.
Tambien debo tener un ejemplo de la unit test de python en algun sitio del HDD, si lo encuentro, lo añadire.

Los casos de prueba estan muy bien para programas de este tipo, pero que pasa con el resto (que son la gran mayoria). ¿Como se prueba una inferfaz grafica?, ¿Como se le hace un test de regresion a khtml? y un protocolo, ¿que tipo de unit test se empea?.
No acabo de ver muy claro como se prueba el resto de software de manera automatica, asi que entiendo que haya mucha gente que no vea una utilidad clara a las unittest

Iba a poner esto en el wiki de lilo, pero el viernes se piño, y hasta el lunes no creo que vuelva a la vida. Tenemos problemas serios con el suministro electrico.

< Jornadas de Redes Telecomunicaciones Públicas (2 comments) | ¿Qué le pasa a Libertonia? (23 comments) >
Enlaces Relacionados
· test-driven development
· XP (eXtreme Programming)
· Junit
· phpUnit
· PyUnit
· unittest
· unit++
· cutee
· cut
· cpptest
· cppunit
· yaktest
· check
· unit test en C#
· More on ridiculum's Diary
· Also by ridiculum

Encuesta
¿Como pruebas el software?
· No lo pruebo 0%
· Hago uso de unit test 37%
· Pruebo las cosas a mano 0%
· Lo entrego al cliente y que el me diga lo que no funciona 25%
· A la rubia si que la probaba 37%

Votos: 8
Resultados | Otras Encuestas

Menu
· crear cuenta
· FAQ
· búsqueda
· Fuentes de Noticias

Login
Nueva cuenta
Usuario:
Contraseña:

Ver: Modo: Orden:
Unit test | 13 comentarios (13 temáticos, editoriales, 0 ocultos)
Pasar los Tests Unitarios != programa que funciona (3.00 / 1) (#1)
por jamarier a las Mon May 17th, 2004 at 12:54:19 AM CET
(Información Usuario) http://barbacana.net/drupal/

No hace mucho tiempo, he estado mirando cosas sobre los test unitarios. Lo cierto y verdad es que no sirven para comprobar si un programa funciona. Un amigo mio metido en cuestiones de verificabilidad de programas dice que a menos que demuestres que funcionan bien para todas las posibilidades no puedes demostrar que este cumple.

¿Para qué sirven entonces los test unitarios? Los test unitarios analizan que el comportamiento de un trozo de código es el que se espera de él (para ciertos datos tipo). Y esto, ¿para que sirve? Yo le veo 2 utilidades básicas (muy relacionadas entre sí):
  • Para marcar el objetivo de nuestro proceso de programación. Si (ejemplo puesto para simplificar) necesitamos crear una función (o clase) que calcule el seno de un ángulo en radianes creamos antes de dicha función los tests que debe superar para considerarse como válido. Así compararíamos la salida de la función para entradas tipo como: 0, pi/6, pi/4, 2*pi/6, ... Repito, esto no garantiza que la función esté bien programada. Permite detectar grandes y no tan grandes errores lógicos en un algoritmo, pero no permite detectar los errores sutiles si no hemos tenido antes la precaución de hacer test para dichas situaciones excepcionales o límites.
  • Detector de efectos secundarios. Tenemos una clase con su respectivo test que la comprueba, si tocamos el código para añadirle funcionalidad, los tests unitarios nos pueden indicar si se ha perdido la compatibilidad con la funcionalidad antigua de la clases (cosa que significa que todos los programas que usaban dicha clase dejarán de funcionar)


Por supuesto, la calidad de dichos test pasa, como ya se ha dicho, por la representatividad de dichos datos muestra. Un test sobre seno que solo compruebe la validez del seno de 0 es de validez nula. comprobar cada 45º o cada 5º nos dará mayor fiabilidad (que nunca certeza).

Mi experiencia en el tema, escasa por otra parte, es que a diferencia de los asserts* de C, funcionan a base de resultados. Es decir, no me importa como se implemente tal comportamiento, con tal de que el resultado obtenido sea el adecuado. Si pasado un tiempo, deseamos cambiar el algoritmo que implementa dicho comportamiento, no es necesario rehacer los test de prueba.

Así que mi conclusión es que los Test Unitarios no sirven para comprobar si funciona un trozo de código, sino más bien son un sistema para comprobar si no funciona (que no es lo mismo ;-)

* El uso de assert que yo he visto pasa por comprobar resultados intermedios en tiempo de ejecución del programa. Se introduce dentro del código fuente (los test son módulos aparte que se ejecutan casi como un programa independiente), y son muy dependientes del algoritmo empleado. Es decir, es un mecanismo de seguridad en ejecución: «Si esta variable fuese negativa el destrozo sería tan grande que es mejor que el programa se pare». Inserto un assert y a otra cosa.

-----
- Porque mañana será un gran día.



Mis respuestas (none / 0) (#7)
por man ls a las Tue May 18th, 2004 at 11:54:30 PM CET
(Información Usuario)

Por partes:
Los casos de prueba estan muy bien para programas de este tipo, pero que pasa con el resto (que son la gran mayoria). ¿Como se prueba una inferfaz grafica?,
Hay dos técnicas básicas:
  • Una es escribir todo tu programa de forma que reciba los eventos desde fuera (algo así como un modelo-vista-controlador). Para hacer la prueba unitaria, sólo tienes que crear estos eventos y enviarlos a tu programa.
  • la segunda es utilizar una herramienta específica, que arranque el programa y le mande los eventos desde fuera. Por motivos de trabajo (quería trucar el flash del yeti atizando al pingüino), encontré un montón de herramientas parecidas para Windows -- supongo que para Linux habrá otras tantas.
Yo prefiero la primera opción: creo la secuencia de eventos en código, y se la endiño al programa. Además así puedo comprobar el tamaño de las ventanas y demás parámetros. El único problema es que el código puede volverse demasiado complejo.
¿Como se le hace un test de regresion a khtml?
Mi prueba básica estándar es: abrir un conjunto de páginas con khtml, y comprobar que no se cuelga. Sólo esto ya es algo.

Para los infinitos problemas de paginación que pueden surgir, lo mejor es hacerlo (una vez más) desde dentro: conocer las estructuras internas que crea khtml, y comprobar que ante una salida, se crean los objetos esperados. Es muy dependiente del código en particular, y como tal potente y complejo.
y un protocolo, ¿que tipo de unit test se empea?.
En el trabajo hago un montón de aplicaciones web. Hay herramientas muy especializadas, como HttpUnit, que me permiten enviar peticiones http específicas, y comprobar que (por ejemplo) la salida contiene las palabras clave esperadas. O que la página generada no contiene la temible palabra exception. HttpUnit está escrito en Java, pero sirve para probar cualquier aplicación web.

Pero tengo que reconocer que casi nunca uso estas cosas. Como veréis, soy bastante fan del "unitario" en "prueba unitaria": ya que al final siempre tengo un servlet, lo que hago es crearme objetos ServletRequest y ServletResponse, que es como se encapsulan en Java la entrada y la salida de una petición http: les añado el contenido adecuado para la petición, y compruebo que la respuesta es la que me interesa. Es rápido (no tengo que arrancar el contenedor Tomcat), limpio (mi código es independiente del contenedor) y preciso (no busco que contenga "pepe", busco que sea exactamente el formato esperado).
No acabo de ver muy claro como se prueba el resto de software de manera automatica, asi que entiendo que haya mucha gente que no vea una utilidad clara a las unittest
En mi modesta opinión, y como ya le respondí a xsidius: esto son más bien pruebas de integración, en las que se prueba un programa entero (desde la petición hasta la respuesta). No es que esté mal, pero es un concepto distinto -- pruebas de caja negra. Y las herramientas puede que tengan que ser un poco distintas.

Yo me doy por contento cuando puedo probar las funciones individuales. Que la cajita salga más arriba o más abajo me importa menos; pero un casque gordo es más grave. En cualquier caso, ambos conjuntos de prueba tienen su uso.



Unit test | 13 comentarios (13 temáticos, editoriales, 0 ocultos)
Ver: Modo: Orden:

ecol Logo Powered by Scoop
Todas las Marcas Registradas y copyrights de esta página son propiedad de sus respectivos dueños.
Los comentarios son propiedad del que los escribe.
Los iconos de las noticias y el logotipo son propiedad de Javier Malonda.
El Resto © 2002 Escomposlinux.org y aledaños.

Puedes sindicar los contenidos de libertonia en formato RSS 1.0 y RDF 0.9. También se puede sindicar la cola de envíos pendientes de moderación.

El proyecto escomposlinux.org está dedicado a la memoria de tas

crear cuenta | faq | búsqueda