03 Brownfield-Übungen: KI-gestütztes Testen in Locust
March 2026 (2177 Words, 13 Minutes)
Überblick
In dieser Übung nutzen Sie KI-Unterstützung, um Tests zu schreiben, die die Code-Coverage verbessern — in einem echten Open-Source-Projekt: Locust, einem Python-Lasttest-Framework. Sie arbeiten in Teams von 3-5 Studierenden. Alle Teams arbeiten am selben Paket (Paket A — kleine, in sich geschlossene Python-Hilfsfunktionen), sodass Sie danach die Ansätze der Teams untereinander vergleichen können.
Die Kernregel: Jeder KI-Prompt, der Code erzeugt, bekommt einen eigenen Commit, der sowohl die Codeänderungen als auch einen Tagebucheintrag enthält. Dies zwingt Sie, in kleinen, dokumentierten Schritten zu arbeiten — genau so funktioniert KI-gestützte Entwicklung in der Praxis.
Falls Sie die Locust Exploration-Übung bereits absolviert haben, sind die Setup-Schritte 1-6 möglicherweise schon erledigt. Überfliegen Sie sie und setzen Sie dort fort, wo Sie aufgehört haben.
Prompts selbst gestalten: Sie sollen Ihre eigenen Prompts für die KI formulieren. Denken Sie an Vorlesung 2 — From Copy-Paste to Crafted Prompts und wenden Sie an, was Sie über strukturiertes, kontextreiches Prompting gelernt haben. Ihr Tagebuch soll die verwendeten Prompts und Ihre Iterationen dokumentieren.
Setup
-
Repository klonen — wir arbeiten mit einer Lehrkopie in unserer Kursorganisation:
git clone https://github.com/hs-aalen-software-engineering/locust.git cd locust -
Abhängigkeiten installieren:
uv syncFalls dies fehlschlägt, häufige Ursachen sind:
uvnicht installiert (docs.astral.sh/uv), fehlendes Python 3.10+ odergevent-Build-Fehler unter Windows. Nutzen Sie Ihr KI-Tool zum Debuggen.Achtung: Auch ausgereifte Open-Source-Projekte haben manchmal unvollständige Dependency-Dateien. Wenn
uv sync— oder später pytest — über ein fehlendes Modul meckert, sind Sie möglicherweise auf einen echten Bug inpyproject.toml/uv.lockgestoßen. Beheben Sie es lokal mituv add <modul>und halten Sie den Fund im Tagebuch fest. Das ist ein legitimer Beitrag. -
Pre-Commit Hooks aktivieren — diese führen bei jedem Commit ruff (Linter + Formatter) aus:
pip install pre-commit pre-commit install -
pytest verifizieren — führen Sie die Paket-A-Testdatei aus, um Ihr Setup zu bestätigen:
uv run pytest locust/test/test_util.py -x -qWenn das durchläuft, ist Ihr Setup in Ordnung.
-
Feature-Branch erstellen:
git checkout -b feature/<team-name>-util-coverage # Beispiel: feature/team3-util-coverage -
Tagebuch-Ordner erstellen:
mkdir -p diary/feature/<team-name>-util-coverage
So arbeiten Sie: Commits und Tagebuch
Die Ein-Prompt-ein-Commit-Regel
Jeder Prompt, der Code erzeugt, bekommt einen eigenen Commit, der sowohl die Codeänderungen als auch den Tagebucheintrag enthält. So sieht ein Zyklus aus:
- Sie schreiben einen Prompt und geben ihn der KI.
- Die KI generiert Code. Sie bewerten ihn, bearbeiten ihn, führen Tests aus.
- Sie schreiben einen Tagebucheintrag (z.B.
002-coverage-memoize.md) mit Prompt, Tool, Modell und Anzahl der Iterationen. - Sie führen
git addfür sowohl die Testdatei als auch die Tagebuchdatei aus. - Sie committen:
git commit -m "[diary] 002 — Tests for util/cache.py memoize()"
Das ist der Arbeitsablauf für jede Aufgabe in dieser Übung. Keine Ausnahmen.
Vorlage für Tagebucheinträge
Jeder Tagebucheintrag folgt diesem Format:
# NNN — Short Title
**Date**: YYYY-MM-DD
**Tool**: [Claude Code / GitHub Copilot / ...]
**Model**: [model name]
**Iterations**: [number of follow-up prompts]
## Prompt
**YYYY-MM-DD HH:MM**
[The full prompt text as given to the AI.]
Ordnerstruktur des Tagebuchs
diary/feature/<team-name>-util-coverage/
├── 001-onboarding.md
├── 002-coverage-memoize.md
├── 003-coverage-retry.md
└── 004-coverage-calc-distribution.md
Warum das wichtig ist
Das Tagebuch IST der Nachweis KI-gestützter Arbeit. Ein PR mit guten Tests, aber ohne Tagebucheinträge bekommt null Punkte für das Kriterium “PR- und Tagebuchqualität” (25 % der Note). Der Prozess zählt mehr als die Politur. Ein gut dokumentierter Versuch mit Korrekturen und Reflexionen ist mehr wert als ein perfektes Ergebnis ohne Nachweis, wie Sie dorthin gelangt sind.
Definition of Done
Jeder PR ist vollständig, wenn:
- Alle bestehenden Tests bestehen (keine Regressionen)
- Neue Tests prüfen tatsächliches Verhalten (nicht nur Zeilenabdeckung)
- Der Code den Linter besteht — aktivieren Sie Pre-Commit Hooks (
pre-commit install) oder verwenden Sie die ruff VS Code Extension, oder führen Sieuv run ruff check . && uv run ruff format --checkmanuell aus - Commit-Nachrichten klar und aussagekräftig sind
- Tagebucheinträge den vollständigen Prozess dokumentieren
Paket-A-Anleitung: util/cache.py — Schritt für Schritt
Arbeiten Sie diese Anleitung als Team durch. Sie führt eine Datei komplett durch, damit alle den vollständigen Ablauf sehen, bevor Sie sich aufteilen und die restlichen Dateien selbstständig bearbeiten.
Dateifokus: locust/util/cache.py — der memoize()-Dekorator (derzeit nur 10 % Zeilenabdeckung).
Schritt 1: Aktuelle Coverage messen
Führen Sie den Coverage-Befehl aus — beachten Sie die zwei --cov-report-Flags. Der html-Report ist derjenige, auf den es ankommt:
uv run pytest locust/test/test_util.py locust/test/test_log.py locust/test/test_taskratio.py \
--cov=locust/util --cov=locust/log \
--cov-report=term-missing --cov-report=html -q
Die Terminal-Tabelle zeigt pro Datei einen Cover-Prozentsatz und Missing-Zeilennummern. Das ist für einen schnellen Überblick nützlich — aber der HTML-Report ist der Ort, an dem Sie wirklich lernen, was fehlt:
- Öffnen Sie
htmlcov/index.htmlim Browser (einfach doppelklicken, oderstart htmlcov/index.htmlunter Windows). - Klicken Sie auf
locust/util/cache.py. - Nicht abgedeckte Zeilen sind rot markiert. Teilweise abgedeckte Branches sind gelb. Abgedeckte Zeilen sind grün.
Halten Sie diese Seite in einem Tab offen. Sie kommen nach jedem Testlauf hierher zurück.
Schritt 2: Den zu testenden Code lesen
Öffnen Sie locust/util/cache.py. Suchen Sie den memoize()-Dekorator. Es ist ein reiner Python-Dekorator ohne externe Abhängigkeiten — Sie verstehen ihn in 5 Minuten.
Worauf Sie achten sollten:
- Er akzeptiert ein
timeout-Argument (wie lange ein gecachter Wert als aktuell gilt). - Er akzeptiert ein
dynamic_timeout-Flag, das das Timeout bei jedem Cache-Miss verdoppelt. - Die gewrappte Funktion bekommt eine
clear_cache()-Methode angeheftet. - Der Cache-Schlüssel wird aus den Funktionsargumenten gebildet.
Sie müssen den Code gut genug verstehen, um beurteilen zu können, ob die Tests der KI korrekt sind. Wenn Sie diesen Schritt überspringen und der KI-Ausgabe blind vertrauen, werden Sie Tests erhalten, die plausibel aussehen, aber nichts Sinnvolles testen.
Schritt 3: Tests für memoize() schreiben
Nutzen Sie jetzt Ihr KI-Tool. Schreiben Sie einen Prompt, der Folgendes enthält:
- Den Namen und Standort des Dekorators (
memoize()inlocust/util/cache.py). - Was der Dekorator tut (cached den Rückgabewert einer Funktion für
timeoutSekunden, verdoppelt optional das Timeout bei jedem Miss, stellt eineclear_cache()-Methode bereit). - Die spezifischen Fälle, die Sie getestet haben möchten:
- Cache-Miss (erster Aufruf führt die Funktion aus)
- Cache-Hit (zweiter Aufruf mit gleichen Argumenten gibt den gecachten Wert zurück, ohne neu auszuführen)
- Timeout-Ablauf (nach
timeoutSekunden führt der nächste Aufruf erneut aus) dynamic_timeout-Verdopplung (verifizieren, dass das Timeout nach jedem Miss wächst)clear_cache()-Methode auf der gewrappten Funktion
Verwenden Sie keinen vagen Prompt. Hier ist ein Beispiel, was Sie NICHT tun sollten:
Generate tests to get 100% coverage on cache.py.
Warum das schlecht ist: Die Optimierung auf Coverage-Prozente erzeugt bedeutungslose Tests. Die KI generiert Tests, die die Funktion mit trivialen Eingaben aufrufen und True asserten — technisch werden Zeilen abgedeckt, aber nichts wird verifiziert. Ein Test, der keinen echten Bug fängt, ist wertlos, egal wie viele Zeilen er “abdeckt.”
Seien Sie stattdessen konkret darüber, welches Verhalten jeder Test verifizieren soll. Die KI produziert besseren Code, wenn Ihr Prompt das Was und Warum beschreibt, nicht nur “mach die Zahl größer.”
Schritt 4: Die KI-Ausgabe bewerten
Bevor Sie einen generierten Test akzeptieren, stellen Sie sich drei Fragen:
- Testet er ein spezifisches Verhalten? Ein Test, der
cached_fn()aufruft und assertet, dass der Rückgabewert nichtNoneist, testet nichts Nützliches. - Würde dieser Test fehlschlagen, wenn ein echter Bug eingeführt würde? Wenn jemand die Timeout-Logik so brechen würde, dass Cache-Einträge nie ablaufen, würde dieser Test das erkennen?
- Prüfen die Assertions das Richtige? Die Assertions sollten den Inhalt des Rückgabewerts oder den Seiteneffekt verifizieren (z.B. dass die zugrundeliegende Funktion die erwartete Anzahl an Malen aufgerufen wurde), nicht nur den Typ.
Löschen Sie jeden Test, den Sie nicht erklären können. Besser 3 aussagekräftige Tests als 8 oberflächliche.
Schritt 5: Tests ausführen und Coverage prüfen
Führen Sie zuerst den schnellen Testbefehl aus, um zu verifizieren, dass nichts kaputt ist:
uv run pytest locust/test/test_util.py -x -q
Dann führen Sie den Coverage-Befehl aus Schritt 1 erneut aus:
uv run pytest locust/test/test_util.py locust/test/test_log.py locust/test/test_taskratio.py \
--cov=locust/util --cov=locust/log \
--cov-report=term-missing --cov-report=html -q
Aktualisieren Sie htmlcov/index.html im Browser und klicken Sie erneut auf cache.py. Die vorher roten Zeilen sollten jetzt grün sein. Falls einige noch rot sind, zeigt Ihnen der HTML-Report genau welche — gehen Sie zurück zu Schritt 3 und fügen Sie Tests für diese Fälle hinzu.
Schritt 6: Commit mit Tagebucheintrag
Schreiben Sie Ihren Tagebucheintrag — zum Beispiel diary/feature/<team-name>-util-coverage/002-coverage-memoize.md — unter Verwendung der Vorlage aus dem Tagebuch-Abschnitt oben. Dokumentieren Sie den vollständigen Prompt, das Tool, das Modell und wie viele Iterationen nötig waren.
Dann committen Sie beide Dateien zusammen:
git add locust/test/test_util.py diary/feature/<team-name>-util-coverage/002-coverage-memoize.md
git commit -m "[diary] 002 — Tests for util/cache.py memoize()"
Sowohl die Testdatei ALS AUCH die Tagebuchdatei sind im selben Commit. Das ist die Regel.
Schritt 7: Jetzt bearbeiten Sie die restlichen Dateien selbstständig
Die obige Anleitung hat Ihnen das Muster gezeigt. Wenden Sie die Schritte 2-6 selbstständig auf jede der folgenden Dateien an. Jede Datei bekommt ihren eigenen Tagebucheintrag und ihren eigenen Commit.
Datei 2: locust/util/exception_handler.py — retry()-Dekorator
Aktuelle Coverage: 54 %. Nur der Happy Path ist getestet — die Retry- und Fehlerpfade sind nicht abgedeckt.
Schreiben Sie Tests für:
- Erfolg beim ersten Versuch (kein Retry nötig)
- Erfolg beim Retry (schlägt einmal fehl, gelingt beim zweiten Versuch)
- Fehlschlag nach Ausschöpfung aller Versuche
- Benutzerdefinierte Exception-Filterung
Ziel: 54 % → nahe 100 %.
Datei 3: locust/user/inspectuser.py — _calc_distribution()
Aktuelle Coverage: 60 %. Reine Berechnung mit einfachen User-Klassen-Stubs.
Schreiben Sie Tests für:
- Gleiche Task-Gewichte
- Gemischte Gewichte (einige Tasks höher gewichtet als andere)
fixed_count-User- Randfall null User
Ziel: 60 % → ~80 %.
Nutzen Sie nach jeder Runde den HTML-Coverage-Report, um zu bestätigen, dass die vorher roten Zeilen grün werden.
Schritt 8: Pull Request erstellen
Pushen Sie Ihren Branch und erstellen Sie einen PR gegen master:
git push -u origin feature/<team-name>-util-coverage
Ihre PR-Beschreibung sollte enthalten:
- Was Sie geändert haben und warum
- Coverage-Zahlen vorher und nachher, pro Datei — zum Beispiel:
## Coverage
| Datei | Vorher | Nachher |
|-------|--------|---------|
| util/cache.py | 10 % | 98 % |
| util/exception_handler.py | 54 % | 96 % |
| user/inspectuser.py | 60 % | 81 % |
- Ein Link zu einem Screenshot des HTML-Coverage-Reports (optional, aber nützlich).
Geschätzte Gesamtzeit: 60-75 Minuten mit KI-Unterstützung.
Liefergegenstände
Reichen Sie einen PR gegen master pro Team ein, der Folgendes enthält:
- Neue Testdateien mit Coverage-Verbesserungen
- Coverage-Zahlen (vorher/nachher, pro Datei) in der PR-Beschreibung
- Tagebucheinträge in
diary/feature/<team-name>-util-coverage/, die jede KI-Interaktion dokumentieren - Eine PR-Beschreibung, die erklärt, was Sie geändert haben und warum
Bewertungsmatrix
| Kriterium | Gewichtung | Hervorragend | Ausreichend | Unzureichend |
|---|---|---|---|---|
| Coverage-Verbesserung | 25 % | Alle drei Dateien erreichen die Zielprozentwerte, gemessen am HTML-Report | Zwei von drei Zielen erreicht, eines teilweise | Weniger als zwei Ziele erreicht |
| Testqualität | 35 % | Tests verifizieren Verhalten mit aussagekräftigen Assertions, Randfälle eingeschlossen, würden echte Bugs fangen | Tests verifizieren grundlegendes Verhalten, wenige Randfälle | Tests sind reine Zeilenabdecker — hohe Coverage, geringer Wert |
| Keine Regressionen | 15 % | Alle bestehenden Tests bestehen, Linter besteht (uv run ruff check .), keine Änderungen an bestehenden Testdateien |
Alle Tests bestehen, kleinere Lint-Probleme | Bestehende Tests fehlgeschlagen oder Lint-Fehler |
| PR- und Tagebuchqualität | 25 % | Klare PR-Beschreibung, vollständiges Tagebuch mit Prompts, abgelehnte/akzeptierte KI-Ausgaben dokumentiert, Coverage-Zahlen enthalten | Grundlegende PR-Beschreibung, teilweises Tagebuch | Fehlende PR-Beschreibung oder Tagebuch |
Kurzreferenz
Schneller Testbefehl
uv run pytest locust/test/test_util.py locust/test/test_log.py locust/test/test_taskratio.py -x -q
Laufzeit: ~8 Sekunden.
Coverage-Befehl (mit HTML-Report)
uv run pytest locust/test/test_util.py locust/test/test_log.py locust/test/test_taskratio.py \
--cov=locust/util --cov=locust/log \
--cov-report=term-missing --cov-report=html -q
Danach htmlcov/index.html öffnen.
Hinweis zu vorbestehenden Testfehlern
Einige Tests in der vollständigen Suite schlagen aufgrund von Umgebungsunterschieden fehl (SSL-Zertifikate, Config-Dateipfade, Windows-spezifische Probleme). Keiner davon befindet sich in den Paket-A-Testdateien. Falls Sie einen Fehler in test_log.py:test_greenlet_exception_logger antreffen, handelt es sich um ein bekanntes vorbestehendes Problem — ignorieren Sie es.
Tipps
- Führen Sie den Testbefehl nach jeder Änderung aus. Die schnelle Feedback-Schleife (~8 Sekunden) ist Ihr größter Vorteil.
- Halten Sie
htmlcov/index.htmlin einem Tab offen. Nach jedem Testlauf aktualisieren — das ist der schnellste Weg zu sehen, was noch fehlt. - Lesen Sie zuerst die bestehenden Tests. Die Muster in
test_util.pysind Ihr bester Leitfaden dafür, wie neue Tests aussehen sollten. - Coverage ist ein Mittel, kein Zweck. Ein Test, der eine Funktion aufruft und
Trueassertet, erhöht die Coverage, testet aber nichts. Schreiben Sie Tests, die einen echten Bug fangen würden. - Wenn die KI Tests generiert, bewerten Sie jeden einzelnen. Fragen Sie sich: “Wenn jemand einen Bug in diese Funktion einführen würde, würde dieser Test ihn finden?” Falls die Antwort nein ist, ist der Test wertlos.
- Das Tagebuch IST das Lernartefakt. Der Prozess zählt mehr als die Politur. Ein gut dokumentierter Versuch mit Korrekturen und Reflexionen ist mehr wert als ein perfektes Ergebnis ohne Nachweis, wie Sie dorthin gelangt sind.