Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einleitung
2 Überblick über Python
3 Die Arbeit mit Python
4 Der interaktive Modus
5 Grundlegendes zu Python-Programmen
6 Kontrollstrukturen
7 Das Laufzeitmodell
8 Basisdatentypen
9 Benutzerinteraktion und Dateizugriff
10 Funktionen
11 Modularisierung
12 Objektorientierung
13 Weitere Spracheigenschaften
14 Mathematik
15 Strings
16 Datum und Zeit
17 Schnittstelle zum Betriebssystem
18 Parallele Programmierung
19 Datenspeicherung
20 Netzwerkkommunikation
21 Debugging
22 Distribution von Python-Projekten
23 Optimierung
24 Grafische Benutzeroberflächen
25 Python als serverseitige Programmiersprache im WWW mit Django
26 Anbindung an andere Programmiersprachen
27 Insiderwissen
28 Zukunft von Python
A Anhang
Stichwort

Download:
- ZIP, ca. 4,8 MB
Buch bestellen
Ihre Meinung?

Spacer
 <<   zurück
Python von Peter Kaiser, Johannes Ernesti
Das umfassende Handbuch - Aktuell zu Python 2.5
Buch: Python

Python
gebunden, mit CD
819 S., 39,90 Euro
Galileo Computing
ISBN 978-3-8362-1110-9
Pfeil 21 Debugging
  Pfeil 21.1 Der Debugger
  Pfeil 21.2 Inspizieren von Instanzen – inspect
    Pfeil 21.2.1 Datentypen, Attribute und Methoden
    Pfeil 21.2.2 Quellcode
    Pfeil 21.2.3 Klassen und Funktionen
  Pfeil 21.3 Formatierte Ausgabe von Instanzen – pprint
  Pfeil 21.4 Logdateien – logging
    Pfeil 21.4.1 Das Meldungsformat anpassen
    Pfeil 21.4.2 Logging Handler
  Pfeil 21.5 Automatisiertes Testen
    Pfeil 21.5.1 Testfälle in Docstrings – doctest
    Pfeil 21.5.2 Unit Tests – unittest
  Pfeil 21.6 Traceback-Objekte – traceback
  Pfeil 21.7 Analyse des Laufzeitverhaltens
    Pfeil 21.7.1 Laufzeitmessung – timeit
    Pfeil 21.7.2 Profiling – cProfile
    Pfeil 21.7.3 Tracing – trace


Galileo Computing - Zum Seitenanfang

21.5 Automatisiertes Testen  Zur nächsten ÜberschriftZur vorigen Überschrift

Pythons Standardbibliothek stellt zwei Module zur testgetriebenen Entwicklung (engl. test-driven development) bereit. Unter testgetriebener Entwicklung versteht man eine Art der Programmierung, bei der viele kleine Abschnitte des Programms, sogenannte Units, durch automatisierte Testdurchläufe auf Fehler geprüft werden. Bei der testgetriebenen Entwicklung wird das Programm nach kleineren, in sich geschlossenen Arbeitsschritten so lange verbessert, bis es wieder alle bisherigen und alledazu gekommenen Tests besteht. Auf diese Weise können sich durch das Hinzufügen von neuem Code keine Fehler in alten, bereits getesteten Code einschleichen.

In Python ist das möglicherweise bekannte Konzept der Unit Tests im Modul unittest implementiert. Das Modul doctest ermöglicht es, Testfälle innerhalb eines Docstrings, beispielsweise einer Funktion, unterzubringen. Im Folgenden werden wir uns zunächst mit dem Modul doctest beschäftigen, um danach zum Modul unittest voranzuschreiten.


Galileo Computing - Zum Seitenanfang

21.5.1 Testfälle in Docstrings – doctest  Zur nächsten ÜberschriftZur vorigen Überschrift

Das Modul doctest erlaubt es, Testfälle innerhalb des Docstrings einer Funktion, Methode, Klasse oder eines Moduls zu erstellen, die beim Aufruf der im Modul doctest enthaltenen Funktion testmod getestet werden. Die Testfälle innerhalb eines Docstrings werden dabei nicht in einer komplizierten neuen Definitionssprache verfasst, sondern können direkt aus einer Sitzung im interaktiven Modus in den Docstring kopiert werden.

Beachten Sie bei der Verwendung von Doctests, dass Docstrings auch bzw. hauptsächlich für die Dokumentation beispielsweise einer Funktion gedacht sind. Aus diesem Grund sollten Sie die Testfälle im Docstring möglichst einfach und lehrreich halten, sodass der resultierende Docstring auch in Dokumentationen Ihres Programms verwendet werden kann.

Das folgende Beispiel soll die Verwendung des Moduls doctest anhand der Funktion fak erläutern, die die Fakultät einer ganzen Zahl berechnen und zurückgeben soll.

import doctest 
import math
def fak(n): """ Berechnet die Fakultaet einer ganzen Zahl. >>> fak(5) 120 >>> fak(10) 3628800 >>> fak(20) 2432902008176640000L Es muss eine positive ganze Zahl uebergeben werden. >>> fak(-1) Traceback (most recent call last): ... ValueError: Keine negativen Zahlen! >>> fak(1.5) Traceback (most recent call last): ... ValueError: Keine Gleitkommazahlen! """ res = 1 for i in xrange(2, n+1): res *= i return res
if __name__ == "__main__": doctest.testmod()

Im Docstring der Funktion fak steht zunächst ein erklärender Text. Dann folgt, durch eine leere Zeile davon abgetrennt, ein Auszug aus Pythons interaktivem Modus, in dem Funktionsaufrufe von fak mit ihren Rückgabewerten stehen. Diese Testfälle werden beim Ausführen des Tests nachvollzogen und entweder für wahr oder für falsch befunden.

Auf diese einfachen Fälle folgen, jeweils durch eine Leerzeile eingeleitet, ein weiterer erklärender Text sowie zwei Ausnahmefälle, in denen eine negative Zahl bzw. eine Gleitkommazahl übergeben wurden. Beachten Sie, dass Sie den Stacktrace eines auftretenden Tracebacks im Docstring weglassen können. Auch die im Beispiel stattdessen geschriebenen Auslassungszeichen sind optional.

Die beiden letzten Testfälle wurden in der Funktion noch nicht berücksichtigt, sodass diese im nun durchzuführenden Test fehlschlagen sollten. Um den Test zu starten, muss die Funktion testmod des Moduls doctest aufgerufen werden. Aufgrund der if-Abfrage

if __name__ == "__main__": 
    doctest.testmod()

wird diese Funktion immer dann aufgerufen, wenn die Programmdatei direkt ausgeführt wird. Der Test wird hingegen nicht durchgeführt, wenn die Programmdatei von einem anderen Python-Programm als Modul eingebunden wird.

Im provozierten Fehlerfall lautet das Testresultat folgendermaßen. Zur Rettung des Regenwaldes haben wir uns hier auf das Abdrucken eines der fehlgeschlagenen Testfälle beschränkt.

***************************************************************** 
File "fak.py", line 21, in __main__.fak 
Failed example: 
    fak(1.5) 
Expected: 
    Traceback (most recent call last): 
    ... 
    ValueError: Keine Gleitkommazahlen! 
Got: 
    1 
***************************************************************** 
1 items had failures: 
   2 of   5 in __main__.fak 
***Test Failed*** 2 failures.

Wenn wir die Funktion dahingehend korrigieren, dass sie im Falle unpassender Parameter die erwarteten Exceptions wirft:

def fak(n): 
    """ 
        […] 
    """ 
    if n < 0: 
        raise ValueError("Keine negativen Zahlen!")
if math.floor(n) != n: raise ValueError("Keine Gleitkommazahlen!")
res = 1 for i in xrange(2, n+1): res *= i return res

werden bei erneutem Durchführen des Tests keine Fehler mehr angezeigt. Um genau zu sein: Es wird überhaupt nichts angezeigt. Das liegt daran, dass generell nur fehlgeschlagene Testfälle auf dem Bildschirm ausgegeben werden. Sollten Sie auch auf die Ausgabe geglückter Testfälle bestehen, können Sie die Programmdatei mit der Option -v (für verbose) starten.

Bei der Verwendung von Doctests sollten Sie unbedingt beachten, dass die in den Docstrings geschriebenen Vorgaben Zeichen für Zeichen mit den Ausgaben der intern ausgeführten Testfälle verglichen werden. Dabei sollten Sie immer im Hinterkopf behalten, dass die Ausgaben bestimmter Datentypen nicht immer gleich sind. So stehen beispielsweise die Schlüssel/Wert-Paare eines Dictionarys in keiner garantierten Reihenfolge, sodass Sie innerhalb eines Doctests nie ein Dictionary als Ergebnis ausgeben sollten. Des Weiteren gibt es Informationen, die vom Interpreter oder anderen Gegebenheiten abhängen, beispielsweise entspricht die Identität einer Instanz intern ihrer Speicheradresse und wird sich deswegen natürlich beim Neustart des Programms ändern.

Eine weitere Besonderheit, auf die Sie achten müssen, ist, dass eine Leerzeile in der erwarteten Ausgabe einer Funktion durch den String <BLANKLINE> gekennzeichnet werden muss, da eine Leerzeile als Trennung zwischen Testfällen und Dokumentation fungiert.

Flags

Um einen Testfall genau an Ihre Bedürfnisse anzupassen, können Sie sogenannte Flags vergeben. Das sind Einstellungen, die Sie aktivieren oder deaktivieren können. Ein Flag wird in Form eines Kommentars hinter den Testfall im Docstring geschrieben. Wird das Flag von einem Plus (+) eingeleitet, wird es aktiviert, bei einem Minus (-) deaktiviert. Bevor wir zu einem konkreten Beispiel kommen, sollen die beiden wichtigsten Flags eingeführt werden.


Tabelle 21.4  Doctest-Flags
Flag Bedeutung
ELLIPSIS

Wenn dieses Flag gesetzt ist, kann die Angabe ... für eine beliebige Ausgabe einer Funktion verwendet werden. So können veränderliche Angaben wie Speicheradressen oder Ähnliches in größeren Ausgaben überlesen werden.

NORMALIZE_WHITESPACES

Wenn dieses Flag gesetzt ist, werden Whitespace-Zeichen nicht mit in den Ergebnisvergleich einbezogen. Das ist besonders dann interessant, wenn man ein langes Ergebnis auf mehrere Zeilen umbrechen möchte.


In einem einfachen Beispiel möchten wir den Doctest der bereits bekannten Fakultätsfunktion um die Berechnung der Fakultät einer relativ großen Zahl erweitern. Da es müßig wäre, alle Stellen des Ergebnisses im Doctest anzugeben, soll die Zahl mithilfe des Flags ELLIPSIS gekürzt angegeben werden:

def fak(n): 
    """ 
        Berechnet die Fakultaet einer ganzen Zahl. 
 
        >>> fak(1000) # doctest: +ELLIPSIS 
        402387260077093773543702...000L 
    """ 
    res = 1 
    for i in xrange(2, n+1): 
        res *= i 
    return res
if __name__ == "__main__": doctest.testmod()

Das Setzen des Flags wurde fett hervorgehoben.

Bleibt noch zu sagen, dass insbesondere die Funktion testmod eine Fülle von Möglichkeiten bietet, die Testergebnisse im Programm zu verwenden oder den Prozess des Testens an Ihre Bedürfnisse anzupassen. Sollten Sie daran interessiert sein, bietet sich die Python-Dokumentation an, in der die Funktion besprochen wird.


Galileo Computing - Zum Seitenanfang

21.5.2 Unit Tests – unittest  topZur vorigen Überschrift

Das zweite Modul zur testgetriebenen Entwicklung heißt unittest und ist ebenfalls in der Standardbibliothek enthalten. Das Modul unittest implementiert die Funktionalität des aus Java bekannten Moduls JUnit, das den De-facto-Standard zur testgetriebenen Entwicklung in Java darstellt.

Der Unterschied zum Modul doctest besteht darin, dass die Testfälle bei unit test außerhalb des eigentlichen Programmcodes in einer eigenen Programmdatei in Form von regulärem Python-Code definiert werden. Das vereinfacht die Ausführung der Tests und hält die Programmdokumentation sauber. Umgekehrt ist mit dem Erstellen der Testfälle allerdings mehr Aufwand verbunden.

Um einen neuen Testfall bei unittest zu erstellen, muss eine von der Basisklasse unittest.TestCase abgeleitete Klasse erstellt werden, in der einzelne Testfälle als Methoden implementiert sind. Die folgende Klasse implementiert die gleichen Testfälle, die wir im vorherigen Kapitel mit dem Modul doctest durchgeführt haben. Dabei muss die zu testende Funktion fak in der Programmdatei fak.py implementiert sein, die von unserer Test-Programmdatei als Modul eingebunden wird.

import unittest 
import fak
class MeinTest(unittest.TestCase):
def testberechnung(self): self.failUnlessEqual(fak.fak(5), 120) self.failUnlessEqual(fak.fak(10), 3628800) self.failUnlessEqual(fak.fak(20), 2432902008176640000L)
def testausnahmen(self): self.failUnlessRaises(ValueError, fak.fak, -1) self.failUnlessRaises(ValueError, fak.fak, 1.5)
if __name__ == "__main__": unittest.main()

Es wurde eine Klasse namens MeinTest erzeugt, die von der Basisklasse unit test.TestCase erbt. In der Klasse MeinTest wurden zwei Testmethoden namens testberechnung und testausnahmen implementiert. Beachten Sie, dass der Name solcher Testmethoden mit test beginnen muss, damit sie später auch tatsächlich zum Testen gefunden und ausgeführt werden.

Innerhalb der Testmethoden werden die Methoden failUnlessEqual bzw. fail UnlessRaises verwendet, die den Test fehlschlagen lassen, wenn die beiden angegebenen Werte nicht gleich sind bzw. wenn die angegebene Exception nicht geworfen wurde.

Um den Testlauf zu starten, wird die Funktion unittest.main aufgerufen. Die Fallunterscheidung

if __name__ == "__main__": 
    unittest.main()

bewirkt, dass der Unit Test nur durchgeführt wird, wenn die Programmdatei direkt ausgeführt wird, und ausdrücklich nicht, wenn die Programmdatei als Modul in ein anderes Python-Programm importiert wurde. Die aufgerufene Funktion unittest.main erzeugt, um den Test durchzuführen, Instanzen aller Klassen, die im aktuellen Namensraum existieren und von unittest.TestCase erben. Dann werden alle Methoden dieser Instanzen aufgerufen, deren Namen mit test beginnen.

Die Ausgabe des Beispiels lautet im Erfolgsfall:

.. 
----------------------------------------------------------------- 
Ran 2 tests in 0.000s 
 
OK

Dabei stehen die beiden Punkte zu Beginn für zwei erfolgreich durchgeführte Tests. Ein fehlgeschlagener Test würde durch ein F gekennzeichnet.

Im Fehlerfall wird die genaue Bedingung angegeben, die zum Fehler geführt hat:

.F 
================================================================= 
FAIL: testberechnung (__main__.MeinTest) 
----------------------------------------------------------------- 
Traceback (most recent call last): 
  File "testen.py", line 7, in testberechnung 
    self.failUnlessEqual(fak.fak(5), 12) 
AssertionError: 120 != 12 
 
----------------------------------------------------------------- 
Ran 2 tests in 0.001s 
 
FAILED (failures=1)

Die Klasse TestCase

An dieser Stelle sollen die wichtigsten Methoden der Klasse TestCase des Moduls unittest besprochen werden. Die hier vorgestellten Methoden können entweder in einer von TestCase abgeleiteten Klasse implementiert werden oder können für die Tests selbst aufgerufen werden.

Im Folgenden sei tc eine Instanz der Klasse TestCase.

tc.setUp()

Die Methode setUp wird vor jedem Aufruf einer der implementierten Testmethoden aufgerufen und kann somit für den Test benötigten Intialisierungscode enthalten. Eine in der Methode setUp geworfene Exception wird als Error in den Testbericht eingetragen, und der Test wird abgebrochen.

tc.tearDown()

Die Methode tearDown wird nach jedem Aufruf einer der implementierten Testmethoden aufgerufen und kann somit abschließenden Code enthalten. Eine in der Methode tearDown geworfene Exception wird als Error in den Testbericht eingetragen.

Die nun folgenden Methoden können innerhalb der Testmethoden verwendet werden, um die Testbedingungen festzulegen. Beachten Sie, dass es jeweils zwei äquivalente Methoden gibt, eine assert- und eine fail-Methode. Der Unterschied liegt allein in der Namensgebung, in der die Bedeutung der Methode für den Testfall entweder positiv (assert-Methoden) oder negativ (fail-Methoden) formuliert ist.

tc.assert_(expr[, msg]), tc.failUnless(expr[, msg])

Der Test schlägt fehl, wenn expr False ergibt. Der optionale Parameter msg kennzeichnet die Beschreibung, die für diesen Fehler angezeigt werden soll. Jede der kommenden Methoden verfügt über diesen Parameter und verwendet ihn mit der gleichen Bedeutung. Wir werden daher im Folgenden nicht mehr auf den Parameter msg eingehen.

tc.assertEqual(first, second[, msg]), tc.failUnlessEqual(first, second[, msg])

Der Test schlägt fehl, wenn die Parameter first und second nicht gleich sind. Beachten Sie, dass auf Gleichheit und nicht auf Identität geprüft wird.

tc.assertNotEqual(first, second[, msg]), tc.failIfEqual(first, second[, msg])

Der Test schlägt fehl, wenn die Parameter first und second gleich sind. Beachten Sie, dass auf Gleichheit und nicht auf Identität geprüft wird.

tc.assertAlmostEqual(first, second[, places[, msg]]) tc.failUnlessAlmostEqual(first, second[, places[, msg]])

Rundet first und second auf places Stellen genau und vergleicht die beiden Werte. Der Test schlägt fehl, wenn die gerundeten Werte nicht gleich sind.

tc.assertNotAlmostEqual(first, second[, places[, msg]]), tc.failIfAlmostEqual(first, second[, places[, msg]])

Rundet first und second auf places Stellen genau und vergleicht die beiden Werte. Der Test schlägt fehl, wenn die gerundeten Werte gleich sind.

tc.assertRaises(exception, callable, …), tc.failUnlessRaises(exception, callable, …)

Der Test schlägt fehl, wenn die Funktion callable nicht die Exception exception wirft, falls sie mit den Parametern aufgerufen wird, die anstelle der Auslassungszeichen (…) stehen.

tc.failIf(expr[, msg])

Der Test schlägt fehl, wenn expr True ergibt.

tc.fail([msg])

Der Test schlägt bedingungslos fehl.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






 <<   zurück
  
  Zum Katalog
Zum Katalog: Python






Python
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Linux






 Linux


Zum Katalog: Ubuntu GNU/Linux






 Ubuntu GNU/Linux


Zum Katalog: Praxisbuch Web 2.0






 Praxisbuch Web 2.0


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Praxisbuch Objektorientierung






 Praxisbuch Objektorientierung


Zum Katalog: Einstieg in SQL






 Einstieg in SQL


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für Fachinformatiker


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de