Action unknown: copypageplugin__copy

Testing

Wird ein Software-Projekt etwas grösser, so steigt die Wahrscheinlichkeit, irgendwo einen Fehler einzubauen, rasant an. Zum Beispiel lösen wir irgendwo im Code ein Problem, aber vergessen, dass die Änderung an einer anderen Stelle zu einem neuen Bug führt.

Um dem entgegenzuwirken, schreiben wir Tests: Separater Code, der den eigentlichen Produktiv-Code ausführt und sicherstellt, dass die Resultate unseren Erwartungen entsprechen.

Ein Unit Test überprüft die Funktionsweise eines Moduls ($\approx$ einer Python-Datei).

Wir haben folgenden Python-Code, den wir testen wollen:

sorting.py
def is_sorted(liste):
    index = 0
    while index < len(liste) - 1:
        a = liste[0]
        b = liste[1]
        if b < a:
            return False
        index = index + 1
 
    return True

Aber stimmt der Code auch wirklich? Wir schreiben einen ersten Test, wobei Tests normalerweise in Dateien mit dem Präfix test_ gespeichert werden:

test_sorting.py
from sorting import *
 
assert is_sorted(['Apfel', 'Birne', 'Zwetschge'])
assert not is_sorted(['Birne', 'Apfel', 'Zwetschge'])

Die assert Anweisung (ohne Klammern!) überprüft, ob der folgende Ausdruck wahr ist, also zu True evaluiert. Die erste Zeile überprüft, ob die Liste als sortiert erkannt wird, die zweite, ob herausgefunden wird, dass die Liste nicht sortiert ist.

Wir führen den Test aus mit python test_sorting.py - scheint alles in Ordnung zu sein.

Du kriegst einen Bug-Report eines erbosten Benutzers deiner sorting Bibliothek. Die Liste ['Apfel', 'Zwetschge', 'Birne'] sei fälschlicherweise als sortiert erkannt worden! Natürlich könnten wir jetzt direkt das Problem im Code suchen - aber wer stellt sicher, dass das Problem nicht plötzlich wieder auftaucht? Genau, ein Test.

Im Test-Driven-Development suchen wir nicht zuerst den Fehler, sondern schreiben zuerst einen neuen Test-Case, der den Fehler reproduziert (also beim Test-Durchlauf fehlschlägt). Erst dann flicken wir den fehlerhaften Code, bis alle Tests wieder grün sind.

Aufgabe A

Schreibe einen Test-Case für den rapportierten Fehler. Flicke anschliessend die is_sorted Funktion, bis alle Tests wieder durchlaufen.

Um in grösseren Projekten nicht den Überblick zu verlieren, ist es Usus, ein Test-Framework zu verwenden, um alle Tests im Projekt auszuführen. Die beste Wahl für Python ist pytest.

Installation: python -m pip install pytest

Pytest sucht überall im Ordner nach Dateien, die mit test_ beginnen, und führt darin alle Funktionen aus, die mit dem gleichen Präfix test_ anfangen.

Wir müssen dafür also unseren Test leicht verändern:

test_sorting.py
from sorting import *
 
def test_sorted():
    assert is_sorted(['Apfel', 'Birne', 'Zwetschge'])
    assert not is_sorted(['Birne', 'Apfel', 'Zwetschge'])
 
 
def test_sorted_bug():
    assert not is_sorted(['Apfel', 'Zwetschge', 'Birne'])

Die Ausführung von pytest findet die Datei und die zwei Testfunktionen und fasst die Resultate zusammen:

$ pytest
============================================= test session starts ==============================================
platform darwin -- Python 3.11.7, pytest-8.1.0, pluggy-1.4.0
rootdir: /Users/tom/git/ksr_talit_jupyter
collected 2 items                                                                                              
 
test_sorted.py ..                                                                                        [100%]
 
============================================== 2 passed in 0.00s ===============================================

Pytest wird auch von VSCode unterstützt (ev. muss in der Sidebar die Tests-Ansicht geöffnet werden:

Aufgabe B

Ändere deinen Unit-Test, damit er von pytest gefunden wird, und führe die Tests im Terminal und auch in VSCode aus.

Integration Tests sind eine Stufe höher als die Unit Tests angesiedelt; sie testen das Zusammenspiel von mehreren Modulen oder eines ganzen Programms. Weil diese meist länger dauern als Unit Tests, werden Sie seltener ausgeführt.

Die Test-Coverage (oder Testabdeckung) ist ein Wert zwischen 0 und 100%, der beschreibt, wie gross der Anteil unseres Codes ist, der von den Tests überprüft wird.

Statt nach jeder Änderung die Tests laufen zu lassen, kann dies auch automatisiert werden: z.B. werden bei jedem Commit die Tests ausgeführt und der Commit erst ins Repository genommen, wenn sie erfolgreich sind. Auf github kann dies über Continuous Integration und github actions erreicht werden.

  • talit/python_testing.txt
  • Zuletzt geändert: 2024-03-04 14:38
  • von hof