Code verifizieren mittels Unit- und Regressionstests Institut für Numerische Simulation, Universität Bonn Seminarreihe Technische Numerik
Wozu sollen gut sein? Was für Testarten gibt es? Wie funktionieren solche? Test- und Beispiel
Wozu sollen gut sein? Refactoring: Ändern des Codes bei gleichbleibender Funktionalität. Unabhängiger Einbau von Features Verifizierung von Komponenten eines neuen System(teil)s überprüfen Potentielle Quellen von Bugs Änderungen am Code stellen grundsätzlich eine Quelle für Bugs in bereits vorhandenem Code dar! Problem Unabsichtliches Verändern des Interfaces bestimmter Funktionen für neue Features.
Wozu sollen gut sein? Refactoring: Ändern des Codes bei gleichbleibender Funktionalität. Unabhängiger Einbau von Features Verifizierung von Komponenten eines neuen System(teil)s überprüfen Potentielle Quellen von Bugs Änderungen am Code stellen grundsätzlich eine Quelle für Bugs in bereits vorhandenem Code dar! Lösung Regressions- und Komponententests stellen (weitest gehend) sicher, dass die Funktionalität des vorhandenen Codes fortbesteht.
Abstecher: Extreme Programming Test-Driven Development Pair Programming Refactoring Simple Design Website http://www.extremeprogramming.org/
1. Komponenten- oder Unabhängige Routinen Eingebaute Testroutinen Testroutinen im Code, welche eine bestimmte Funktionalität entweder bei Programmbeginn oder auf Wunsch testen. Testroutinen in externem Programm, welches einzig dazu dient, das Funktionieren und gewollte Nicht-Funktionieren sicher zu stellen. Was ist eine Komponente? Eine Klasse oder eine Funktion: class Vector oder MatrixMultiplication().
2. Regressionstests Shell-Skripte Ansammlung von Skripten, die für bestimmte Eingaben bestimmten Ausgaben erwarten. uite Zusammenfassung von Skripten zu einer uite, welche die gesamte Funktionaliät eines Codes in einer gegebenen Fassung automatisiert überprüfen soll. Was bedeutet Regression? lat. re-gregere - zurückgehen, zurückschreiten. Hier: Das repetitive Wiederholen aller Testfälle, um m gliche unabsichtliche Veränderungen aufzuspüren.
Testprinzipien und -philosophien Testentwicklung parallel zur Programmentwicklung (Test-Driven Development). Alle Funktionen sollen getestet werden. Hierarchische Abhängigkeit: Komplexere Funktionen nutzen verfizierte einfachere. Test von Gut- und Fehlfall. pro Bug ein Test: D. h. ein Test für jeden gefundenen Fehler: Und der Fix behebt genau den Alarm dieses.
Wie funktionieren Komponenten- oder? Prinzip Getestet wird das Interface bzw. der Vertrag einer Komponente, nicht der Code der Komponente selbst (Black Box). Arbeitsweise Test-Funktionen mit Zeilen a la ASSERT(... ); Test-Runner, der die Test-Funktionen der Reihe nach ausführt und in OK(...) oder FAILED:... endet.
Wie funktionieren? Prinzip Alle Testfälle einer Software werden wiederholt und automatisiert durchlaufen, um unabsichtliche Veränderungen zu entdecken, umfasst sowohl Komponenten- als auch Integrations- und System-tests. Arbeitsweise Kleine Test-Skripte, die den Code mit bestimmten Eingabeparametern und -files startet und bestimmte Ausgabe erwartet. uite, die die Skripte alle der Reihe nach ausführt und Ausgabe bei Fehler sichert bzw. verwirft, wenn kein Fehler auftritt.
Unvollständige Auflistung Komponenten- für C AutoUnit CUnit... Komponenten- für C++ Boost Test Library CUTE CxxTest... Regressions- für C/C++ Beaker Quelle http://en.wikipedia.org/wiki/list of unit testing frameworks
Beispiel: Test einer Klasse Vector Sechs Files: 1. vector.hpp 2. vector.cpp 3. vectorunittest.hpp 4. vectorunittest.cpp 5. vectornormtest.cpp 6. testrunner.cpp Download Code steht nach Vortragsende auf der Webseite.
Deklarationen in vector.hpp #ifndef VECTOR_HPP_ #define VECTOR_HPP_ /** Vector-Klasse mit * -# Konstruktor * -# Destruktor * -# Funktion Norm(). */ class Vector { public: Vector(); Vector(double a, double b, double c); Vector() {}; double Norm(); private: double x[3]; }; #endif // VECTOR_HPP_
Definitionen in vector.cpp #include <math.h> #include "vector.hpp" Vector::Vector() { x[0] = 0.; x[1] = 0.; x[2] = 0.; }; Vector::Vector(double a, double b, double c) { x[0] = a; x[1] = b; x[2] = c; }; double Vector::Norm() { double result = 0.; for(int i=0;i<3;i++) result += x[i]*x[i]; return (sqrt(result)); };
Unit-Test in vectorunittest.hpp #ifndef VECTORUNITTEST_HPP_ #define VECTORUNITTEST_HPP_ #include <cppunit/extensions/helpermacros.h> #include "vector.hpp" class VectorTest : public ::TestFixture { CPPUNIT_TEST_SUITE( VectorTest ); CPPUNIT_TEST ( NormTest ); CPPUNIT_TEST_SUITE_END(); public: void setup(); void teardown(); void NormTest(); private: Vector *zero; Vector *unit; Vector *two; }; #endif // VECTORUNITTEST_HPP_
Unit-Test in vectorunittest.cpp #include "vector.hpp" #include "vectorunittest.hpp" // Registers the fixture into the registry CPPUNIT_TEST_SUITE_REGISTRATION( VectorTest ); void VectorTest::setUp() { zero = new Vector(0.,0.,0.); unit = new Vector(1.,0.,0.); two = new Vector(2.,0.,0.); }; void VectorTest::tearDown() { delete(zero); delete(unit); delete(two); };
Unit-Test in vectornormtest.cpp #include "vectorunittest.hpp" #include <math.h> /** UnitTest fuer Vector::Norm(). */ void VectorTest::NormTest() { // unity and zero tests CPPUNIT_ASSERT_EQUAL( 0., zero->norm() ); CPPUNIT_ASSERT_EQUAL( 1., unit->norm() ); // besser? CPPUNIT_ASSERT ( fabs(zero->norm()) < 1e-13 ); CPPUNIT_ASSERT ( fabs(1. - unit->norm()) < 1e-13 ); };
Ausführung heber@bespin:>./vectorunittest. OK(1) heber@bespin:>
Auflistung der wichtigsten Befehle In einer Test-Klasse... Test definiert bzw. implementiert: CPPUNIT TEST SUITE (... Test); Deklariert die uite... Test. CPPUNIT TEST (TestFunktion); Deklariert einen Testcase namens Testfunktion. CPPUNIT TEST SUITE END(); Schliesst die Deklaration der uite ab.... Test::setUp(); Wird vor jedem Test aufgerufen.... Test::tearDown(); Wird nach jedem Test aufgerufen.... Test::Testfunktion(); Die eigentliche Testfunktion.
Definition in testsuite.at AT_INIT([VectorTest]) AT_SETUP([Norm]) AT_CHECK([$at_dir/../VectorUnitTest], \ [ignore], [ignore]) AT_CLEANUP
Zusammenfassung der wichtigsten Befehle AT INIT (name) Initialisert die uite und gibt ihr einen Namen AT SETUP (name) Bezeichnet den Anfang einer Testgruppe mit Namen. AT CHECK([cmd, [], [])] Fuehrt cmd aus und testet auf Return code, stdout und stderr koennen ignore d werden oder in File umgeleitet. AT CLEANUP Bezeichnet das Ende einer Testgruppe.
Wikipediaeintrag zu http://de.wikipedia.org/wiki/unit-test Wikipediaeintrag zu http://de.wikipedia.org/wiki/ Homepage von http://sourceforge.net/projects/cppunit/ Tutorial zu http://www.evocomp.de/tutorials/tutorium cppunit/howto tutoria cppunit.html Erklärung zu Einstiegstutorial zu http://www.gnu.org/software/automake/manual/autoconf/using-.html http://www.lrde.epita.fr/~akim/ccmp/doc/gnuprog2/simple-uses-of-.html