190 likes | 376 Views
BUnit.h : Un módulo C++ simple para aprender prueba unitaria de programas. Universidad de Costa Rica Escuela de Ciencias de la Computación e Informática Prof. Adolfo Di Mare adolfo.dimare@ecci.ucr.ac.cr. Agenda de Trabajo. Motivación Doxygen es parte de la solución
E N D
BUnit.h:Un módulo C++ simple paraaprenderpruebaunitaria de programas Universidad de Costa Rica Escuela de Ciencias de la Computación e Informática Prof. Adolfo Di Mare adolfo.dimare@ecci.ucr.ac.cr
Agenda de Trabajo • Motivación • Doxygen es parte de la solución • Formar a los jóvenesparaconvencer a los viejos • Probar y especificar antes de codificar • Arquitectura de BUnit • Especificaciónpormedio de prueba de programas • Formato de la documentaciónDoxygen • Experiencia con estudiantes • Detalles de implementación • Posibilidades de ampliación • Conclusiones • Códigofuente
Programación II • Módulos • Especificación • Reutilización
Doxygen • Dimitri van Heesch • http://www.doxygen.org Es necesarioinculcar en losprogramadores la disciplina de especificar antes de programar, o sea, de diseñar antes de construir, paramejorar la calidad del producto final.
Motivación Así como las bailarinas son felices cuando danzan, los programadores obtienen su gratificación cuando programan. BUnit.h permite usar datos de prueba como ejemplos que complementan la especificación de un módulo.
Motivación • Construir la especificación antes de la implementación. • Incorporar los datos de pruebacomo parte de la especificación de cualquiermódulo.
De los Jóvenes a los Viejos • Es difícil convencer a los profesores • Es difícil convencer a los jefes • Es más fácil lograr que los jóvenes adopten prácticas mejores de trabajo • Los resultados convencen a los viejos
Especificación, Prueba, Codificación • Quien no sabeadóndevá... ¡llega a otrolado! • Agregueunaprueba, hágalafallar, codifique el algoritmoparapasar la prueba y porúltimoelimine la redundancia • Escriba unaprueba en cadaocasión en que se veatentado a usarunainstrucción "print()"
Arquitectura de Bunit.h ┌────────────────┐ │ TestCase │ ├────────────────┤ │ run() = 0 │ │ successCount() │ │ failureCount() │ │ assertTrue() │ ├────────────────┤ │ Fixture │ │ - setUp() │ │ - tearDown() │ └────────────────┘ /\ ││ ┌─────────────────────────┐ │ TestSuite< TestCase > │ ├─────────────────────────┤ │ addTest ( TestCase & ) │ │ addSuite( TestSuite & ) │ └─────────────────────────┘
#include "BUnit.h" // #1 Agregrar#include "BUnit.h" /// Ejemplomínimo de uso de \c BUnit. class test0 : public TestCase { // #2 Derivarde TestCase public: bool run() { assertTrue( 1 + 1 == 3 ); // #3 InvocarassertTrue() return wasSuccessful(); } }; #include <iostream> // std::cout /// Programa principal queejecuta la prueba. int main() { test0 test0_instance; test0_instance.run(); // #4 run(): Ejecutarlaspruebas if ( ! test0_instance.wasSuccessful() ) { std::cout << test0_instance.report(); } return 0; }
Determina si existe un camino en el grafo comenzando en "src" y terminando en "dst". Si src == dst retorna "true" (un vértice siempre está conectado consigo mismo). Retorna "true" cuando el camino existe, y "false" en caso contrario. La lista "C" contiene la secuencia de nodos del camino. Si no hay camino, la lista "C" queda vacía. boolGraph::connected( const std::string & src, const std::string & dst, std::list< std::string > & C ); {{ // test::diagram() A(1) C(1) O(1)---->O(2) / \ / \ /|\ | / \ / \ | | F->--A(2)-->-B-> ->D | | \ / \ / | | \ / \ / | \|/ A(3) C(2) O(4)<----O(3) }} {{ // test::connected() assertTrue( G.connected( "F" , "F", C ) ); // siestáconectado assertTrue( C.size() == 1 && C.front() == "F" ); // porqueyaestáahí assertTrue( ! G.connected( "???" , "???", C ) ); // no existe el vértice assertTrue( ! G.connected( "F" , "O(4)", C ) ); // grafo no conexo assertTrue( C.size() == 0); assertTrue( ! G.connected( "D" , "F" , C ) ); // el grafoesdirigido assertTrue( C.size() == 0); }}
{{ // test::diagram() A(1) C(1) O(1)---->O(2) / \ / \ /|\ | / \ / \ | | F->--A(2)-->-B-> ->D | | \ / \ / | | \ / \ / | \|/ A(3) C(2) O(4)<----O(3) }} {{ // test::connected() std::list< std::string > C; // camino en el grafo std::list< std::string >::iterator it; assertTrue( ! G.connected( "???" , "???", C ) ); // no existe el vértice assertTrue( ! G.connected( "F" , "O(4)", C ) ); // grafo no conexo assertTrue( C.size() == 0); assertTrue( ! G.connected( "D" , "F" , C ) ); // el grafo es dirigido assertTrue( C.size() == 0); assertTrue( G.connected( "F" , "F", C ) ); // si está conectado assertTrue( C.size() == 1 && C.front() == "F" ); // porque ya está ahí assertTrue( ! G.connected( "D", "A(2)" , C ) ); assertTrue( C.size() == 0 ); assertTrue( G.connected( "A(2)" , "D", C ) ); assertTrue( C.size() == 4 ); assertTrue( C.front() == "A(2)" && C.back() == "D" ); it = C.begin(); it++; assertTrue( *it == "B" ); // 2do nodo en el camino it++; assertTrue( *it == "C(1)" || *it == "C(2)" ); // 3er nodo en el camino }}
Documentación Doxygen /** Determina si existe un camino en el grafo comenzando en \c "src" y terminando en \c "dst". - Si <code> src == dst </code> retorna \c "true" (un vértice siempre está conectado consigo mismo). - Retorna \c "true" cuando el camino existe, y \c "false" en caso contrario. - La lista \c "C" contiene la secuencia de nodos del camino. - Si no hay camino, la lista \c "C" queda vacía. \dontinclude test_Graph.cpp \skipline test::diagram() \until }} \skipline test::connected() \until }} \see test_Graph::test_connected() */ bool Graph::connected( const std::string & src , const std::string & dst , std::list< std::string > & C ) { // ... implementación }
Datos de prueba BUnit /// Datos de prueba para los constructores de la clase \c TestCase. void test_BUnit::test_constructor() { {{ // test::constructor() test_BUnit thisTest; assertTrue( string::npos != thisTest.getName().find( "test_BUnit" ) ); assertTrue( thisTest.failureCount() == 0 ); assertTrue( thisTest.countTestCases() == 1 ); assertTrue( thisTest.successCount() == 0 ); assertTrue( thisTest.runCount() == 0 ); assertTrue( thisTest.failureString() == "" ); }} { // Resto de las pruebas test_BUnit thisTest; assertTrue( thisTest.m_pass == 0 ); assertTrue( thisTest.m_failure == 0 ); assertTrue( thisTest.m_name == 0 ); assertTrue( thisTest.m_failureList.empty() ); } }
Experiencia con estudiantes • BUnitesfácil de usar • No se percatan de queestánusandounaherramienta • Se acostumbran a unamejorcalidad en la documentación • HacerlaspruebasBUnitesotra forma de programación • IncorporanDoxygen+BUnit en suprácticadiaria • Luegoaprenden los conceptosrelevantes • Abstracción, Especificación, PruebaUnitaria, etc.
Implementación • assertTrue() estáimplementadocomouna macro C++ • Se usanlas macros predefinidas del compilador __LINE__ y __FILE__ • Se registran en una lista las pruebas que no tienen éxito
Posibilidades de Ampliación • Clase TestResult • Clase TestFixture • Mayor compatibilidad con JUnit • Barra de progreso • Extensión a Java+JavaDoc
Conclusiones • BUnit.h es simple: cabe en un solo archivo de encabezado. • BUnit.h es compatible con herramientas de la familia JUnit. • La pareja BUnit.h+Doxygen permite mejorar la calidad de la especificaciones de módulos
Código fuente • http://www.di-mare.com/adolfo/p/BUnit/BUnit.zip • http://www.di-mare.com/adolfo/p/BUnit/es/ • http://www.di-mare.com/adolfo/p/BUnit/en/ Muchas gracias