Python bietet mehrere Möglichkeiten Unit Tests zu implementieren. Eine davon ist die Verwendung des Moduls unittest. In diesem Artikel befassen wir uns noch einmal mit dem Beitrag zum Thema Zahlen mit Trennzeichen formatieren. In diesem Beitrag haben wir eine Funktion geschrieben, die lange Zahlen bei der Ausgabe mit Trennzeichen formatieren kann. Wir wollen nun diese Funktion auf mögliche Fehler testen.
Im ersten Schritt kopieren wir die Funktion format_number()
in eine eigene Datei, die den Namen format_number.py erhält. Diese Datei stellt unser Modul dar, das wir testen wollen. Für die Testfälle erstellen wir im selben Ordner eine zweite Datei namens python_unittest.py.
Um die Funktionalität von unittest zu verwenden binden wir dieses Modul ein. Des Weiteren importieren wir das zu testende Modul format_number.
1 2 |
import unittest import format_number |
Wir definieren anschließend eine Testklasse sowie eine Methode. Der Name der Methode muss mit dem String "test"
beginnen. Sollte dies nicht der Fall sein, werden die Testfälle innerhalb der Methode nicht ausgeführt. Innerhalb der Methode können wir nun beliebig viele Testfälle definieren. Zur Auswertung von Ergebnissen stehen diverse Methoden zur Verfügung. Für unseren Fall ist vor Allem die Methode assertEqual
von Bedeutung. Damit lassen sich der Rückgabewert von format_number
und ein manuell definierter String auf Gleichheit prüfen. Im ersten Testfall prüfen wir den Fall der Formatierung der Zahl "1000"
. Um die Testfälle bei der direkten Ausführung von python_unittest.py starten zu können, benötigen wir noch eine entsprechende Anweisung am Ende der Datei.
1 2 3 4 5 6 7 8 9 10 11 12 |
# Klasse für Test der Funktion "format_number" im Modul "format_number" class Test_format_number(unittest.TestCase): # Testfunktion definieren def test_format_number(self): # Test direkt ausführen self.assertEqual(format_number.format_number(1000), "1_000") # Unit Tests ausführen, wenn python_unittest.py direkt ausgeführt wird if __name__ == '__main__': unittest.main() |
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Wir haben einen Test erfolgreich ausgeführt. Im nächsten Schritt testen wir einen Fall, den wir bei der Implementierung von format_number nicht explizit berücksichtigt haben – die Übergabe einer negativen Zahl.
1 |
self.assertEqual(format_number.format_number(-100000), "-100_000") |
AssertionError: '-_100_000' != '-100_000'
Wir sehen in der Ausgabe unter anderem die obige Zeile. Der Test ist fehlgeschlagen. In unserer Implementierung haben wir negative Zahlen nicht behandelt. Die Folge ist nun, dass vor der ersten Stelle ein zusätzlicher Unterstrich eingefügt wurde.
Es ist damit zu rechnen, dass weitere Fehler auftreten werden. Die Funktionalität von unittest ist so gestaltet, dass der Testlauf nach dem ersten aufgetretenen Fehler abbricht. Da dies nicht immer gewünscht ist, ist es möglich, die aufgetretenen Ausnahmefehler abzufangen. Zu diesem Zweck definieren wir die Funktion assert_quiet(func, *args)
. Dieser Funktion wird beim Aufruf die Funktion assertEqual
und dessen Parameter übergeben. Ein etwaiger AssertionError
wird abgefangen und die erste Zeile der Fehlermeldung ausgegeben. Nun können wir den vorigen Test mit unserer neuen Funktion noch einmal durchführen.
1 2 3 4 5 6 7 |
# Funktion zum Abfangen der Exception von "self.assertEqual()" def assert_quiet(func, *args): try: func(*args) except AssertionError as err: error_line = str(err).split('\n')[0] print("AssertionError: {0}".format(error_line)) |
1 2 |
# Test ohne Unterbrechung bei Exception durchführen assert_quiet(self.assertEqual, format_number.format_number(-100000), "-100_000") |
AssertionError: '-_100_000' != '-100_000'
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Wir erhalten wieder eine Fehlerausgabe. Diesmal jedoch wurde der Test nicht abgebrochen. Statt dessen ist er weitergelaufen und wurde mit dem Status OK
beendet. Wir können nun weitere Tests implementieren.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Dezimalzahlen testen assert_quiet(self.assertEqual, format_number.format_number(1000), "1_000") assert_quiet(self.assertEqual, format_number.format_number(1000, sep=' '), "1 000") assert_quiet(self.assertEqual, format_number.format_number(1000, sep='abc'), "1abc000") assert_quiet(self.assertEqual, format_number.format_number(1000.12345), "1_000.12345") # Hexadezimal Zahlen testen assert_quiet(self.assertEqual, format_number.format_number(100000, num_type='hex'), "0x1_86_a0") assert_quiet(self.assertEqual, format_number.format_number(1000, num_type='hex', sep='<>'), "0x3<>e8") assert_quiet(self.assertEqual, format_number.format_number(-100000, num_type='hex'), "-0x1_86_a0") # Binärzahlen testen assert_quiet(self.assertEqual, format_number.format_number(100000, num_type='bin'), "0b1_1000_0110_1010_0000") assert_quiet(self.assertEqual, format_number.format_number(1000, num_type='bin', sep=' '), "0b11 1110 1000") assert_quiet(self.assertEqual, format_number.format_number(-1000, num_type='bin', sep=' '), "-0b11 1110 1000") |
AssertionError: '1cba000' != '1abc000'
AssertionError: '1_000_.12_345' != '1_000.12345'
AssertionError: '0x3><e8' != '0x3<>e8'
AssertionError: '0xx1_86_a0' != '-0x1_86_a0'
AssertionError: '0bb11 1110 1000' != '-0b11 1110 1000'
Unsere Tests haben weitere Fehler offenbart. Mit manchen haben wir gerechnet. So funktioniert die Verwendung negativer Zahlen für alle Zahlensysteme nicht korrekt. Etwas überraschend ist jedoch die falsche Reihenfolge der Trennzeichen "abc"
und "<>"
in Zeile eins und drei. Ironischer Weise haben wir bereits im ursprünglichen Beitrag die Trennzeichen "-*-"
erfolgreich getestet. Da dieser String jedoch symmetrisch ist, wurde der Fehler nicht erkannt. Ein Blick in den Code von format_number.py zeigt, dass die Trennzeichen in num_formated
rückwärts eingefügt werden müssen.
Das Beispiel kann folgendermaßen ausgeführt werden (fromat_number.py muss im gleichen Ordner liegen):
python3 python_unittest.py
Alle Programmdateien des Beitrages herunterladen: python_unittest.zip
Hinterlasse jetzt einen Kommentar