Code-Abdeckung erklärt: Leitungs- und Zweigstellenabdeckung und was 100 % wirklich bedeutet
Durch die Abdeckung erfahren Sie, welcher Code bei Ihren Tests ausgeführt wurde. Es sagt Ihnen nicht, ob die Tests etwas überprüft haben. Wir haben ein kleines Modul auf drei Arten gemessen, um den Unterschied zu zeigen.
- Codeabdeckung misst, wie viel von Ihrem Code während Tests ausgeführt wird.
- Die Filialabdeckung ist strenger als die Leitungsabdeckung und nützlicher.
- In unserem Test erreichte eine Suite ohne keine Zusicherungen immer noch eine Abdeckung von 100 %.
- Nutzen Sie die Abdeckung, um ungetesteten Code zu finden, nicht als Qualitätsnachweis.
Ein Team meldet stolz eine Codeabdeckung von 95 % und liefert dann einen Fehler in einer Zeile aus, die „abgedeckt“ wurde. Das ist kein Widerspruch. Coverage beantwortet eine eng gefasste Frage: Wurde dieser Code bei den Tests ausgeführt? Ob sie das Ergebnis überprüft haben, ist eine ganz andere Frage.
Wir haben Python mit cover.py 7.17 und pytest 9.2 verwendet. Alle Zahlen und Berichte unten stammen aus unserem eigenen Lauf. Die Ideen gelten in jeder Sprache.
Was ist Codeabdeckung?
Die Codeabdeckung ist der Prozentsatz Ihres Codes, der ausgeführt wird, während Ihre automatisierten Tests ausgeführt werden. Ein Tool zeichnet auf, welche Zeilen und optional welche Verzweigungen ausgeführt wurden, und meldet dann, was verpasst wurde.
Am nützlichsten ist es umgekehrt. Eine geringe Abdeckung in einem Gebiet zeigt Ihnen mit Sicherheit, dass die Tests diese nicht ausnutzen. Eine hohe Abdeckung sagt Ihnen viel weniger.
Arten der Deckung
| Typ | Frage, es beantwortet |
|---|---|
| Funktion | Wurde jede Funktion mindestens einmal aufgerufen? |
| Zeile (Aussage) | Ist jede Zeile mindestens einmal gelaufen? |
| Zweig | Ist jeder if in beide Richtungen gegangen, wahr und falsch? |
| Zustand | Hat jeder Teil einer kombinierten Bedingung (a and b) beide Werte angenommen? |
| MC/DC | Hat jede Bedingung unabhängig voneinander das Ergebnis verändert? Wird in sicherheitskritischer Software verwendet. |
Jede Stufe ist strenger als die darüber liegende. Die meisten Teams verfolgen die Leitungs- und Abzweigabdeckung. Zustandsabdeckung und MC/DC kommen in Luftfahrt- und Automobilnormen vor, in denen die Werkzeugunterstützung spezialisiert ist.
Der Code, den wir gemessen haben
def shipping_fee(total, is_member, express=False):
if total < 0:
raise ValueError("total cannot be negative")
fee = 0 if total >= 1000 else 50
if is_member:
fee = max(fee - 25, 0)
if express:
fee += 100
return fee
Unsere erste Testdatei hatte zwei Tests:
def test_regular_order():
assert shipping_fee(500, False) == 50
def test_member_express():
assert shipping_fee(500, True, express=True) == 125
Wir haben es mit eingeschalteter Zweigmessung ausgeführt:
coverage run --branch -m pytest test_shipping.py
coverage report -m
Den Bericht lesen
Name Stmts Miss Branch BrPart Cover Missing
---------------------------------------------------------
shipping.py 9 1 6 1 87% 3
- Stmts 9, Miss 1: neun ausführbare Anweisungen, eine wurde nie ausgeführt.
- Zweig 6, BrTeil 1: Drei
ifEntscheidungen führen zu sechs möglichen Ergebnissen. Eine Entscheidung ging immer nur in eine Richtung. - Fehlt 3: Zeile 3, die
raise ValueError, wurde nie ausgeführt. Kein Test hat eine negative Gesamtsumme bestanden.
Der Fix ist ein Test für den Fehlerpfad. Wir haben auch einen Test für einen Großauftrag hinzugefügt:
def test_free_over_1000():
assert shipping_fee(1000, False) == 0
def test_negative_total():
with pytest.raises(ValueError):
shipping_fee(-1, False)
Bei beiden Dateien zeigte der Bericht 100 % und keine fehlenden Zeilen oder Teilverzweigungen. Fehlerpfade sind die klassischen Lückenabdeckungsfunde, weshalb Tests auf ungültige Eingaben, die mit Techniken wie der Äquivalenzpartitionierung entwickelt wurden, so gut damit harmonieren.
Was der Bericht verbarg
Schauen Sie sich den ersten Bericht noch einmal an. Es hieß, nur Zeile 3 fehle. Bei den ersten beiden Tests wurden jedoch insgesamt 500 Stück verwendet, sodass die Regel des kostenlosen Versands für Bestellungen ab 1.000 Stück nie getestet wurde und im Bericht nicht darauf hingewiesen wurde.
Der Grund dafür ist, dass fee = 0 if total >= 1000 else 50 in einer Zeile steht. cover.py misst Verzweigungen zwischen Zeilen, sodass ein bedingter Ausdruck innerhalb einer einzelnen Zeile als eine ausgeführte Anweisung zählt. Wenn diese Regel einen Fehler hätte, z. B. > statt >=, hätte die erste Suite immer noch 87 % ohne Hinweis angezeigt. Tests der Grenze selbst, wie in der Randwertanalyse beschrieben, sind es, die es erfassen.
100 % Deckung ohne Schecks
Als nächstes haben wir einen bewusst schlechten Test geschrieben, der jeden Pfad aufruft, aber nichts behauptet:
def test_runs_everything_but_checks_nothing():
shipping_fee(500, True, express=True)
shipping_fee(1000, False)
shipping_fee(500, False)
try:
shipping_fee(-1, False)
except ValueError:
pass
shipping.py 9 0 6 0 100%
Es wurde eine 100 %ige Leitungs- und Zweigstellenabdeckung erreicht. Die Funktion könnte überhaupt eine beliebige Zahl zurückgeben und dieser Test würde trotzdem bestehen. Dies ist die Kerngrenze der Abdeckung: Sie misst die Ausführung, nicht die Überprüfung.
Tipp: Lesen Sie beim Überprüfen einer Pull-Anfrage die Behauptungen, nicht nur die Änderung der Abdeckung. Ein Test, der die Abdeckung ohne eine aussagekräftige Behauptung hinzufügt, fügt nur falsches Vertrauen hinzu.
Um zu messen, ob Tests tatsächlich Fehler erkennen, verwenden Sie Mutationstests. Ein Tool wie mutmut für Python, PIT für Java oder Stryker für JavaScript nimmt kleine Änderungen an Ihrem Code vor, z. B. das Umdrehen von >= in >, und prüft, ob ein Test fehlschlägt. Unser No-Assert-Test würde jede Mutation überleben lassen.
Welches Ziel gesetzt werden soll
Es gibt keine allgemeingültige Zahl. Praktische Anleitung:
- Verfolgen Sie nicht 100 %. Die letzten paar Prozent bedeuten oft, trivialen Code zu testen oder Tests zu schreiben, die nur Zeilen berühren.
- Viele Teams streben insgesamt etwa 70 bis 85 % an, mit höheren Erwartungen an die Kerngeschäftslogik.
- Beobachten Sie den Trend, nicht die Zahl. Die Abdeckung, die bei jeder Veröffentlichung sinkt, ist ein Warnzeichen.
- Fordern Sie die Abdeckung von neuem Code in Pull-Requests, anstatt alten Code auf einmal zu erzwingen.
- Schließen Sie mit klaren Regeln aus, was unwichtig ist, wie zum Beispiel generierten Code.
Es gilt das Goodhartsche Gesetz: Wenn eine Maßnahme zum Ziel wird, ist sie keine gute Maßnahme mehr. Harte Ziele fördern Tests wie unser No-Assert-Beispiel.
Werkzeuge nach Sprache
| Sprache | Gängige Werkzeuge |
|---|---|
| Python | cover.py, pytest-cov |
| JavaScript / TypeScript | Istanbul (NYC), eingebaut in Jest und Vitest |
| Java | JaCoCo |
| .NET | Bettdecke |
| Geh | Testen Sie das Cover |
| Berichterstattung | Codecov, SonarQube, Overalls |
Abdeckung in CI
Führen Sie die Abdeckung als Teil Ihrer CI-Pipeline durch und veröffentlichen Sie den Bericht, damit Prüfer sehen können, welche neuen Zeilen noch nicht getestet wurden. Bei Coverage.py coverage report --fail-under=80 schlägt der Build unterhalb eines Schwellenwerts fehl. Halten Sie den Schwellenwert niedrig und kombinieren Sie ihn mit einer Regel für neuen Code, damit nicht jede Änderung durch eine einzelne große Legacy-Datei blockiert wird.
Über die Berichterstattung hinaus: Bessere Fragen
- Decken Tests die riskanten, häufig geänderten Teile des Codes ab?
- Werden Grenzen und Fehlerpfade getestet, nicht nur der glückliche Weg?
- Würde ein Test fehlschlagen, wenn diese Zeile falsch wäre?
- Sind die Tests schnell und stabil genug, um bei jeder Änderung ausgeführt zu werden?
Diese gehören in jede Teststrategie. Die Berichterstattung hilft nur bei der Beantwortung des ersten.
Code ausschließen und HTML-Berichte lesen
Manche Codes sind es nicht wert, direkt getestet zu werden, etwa Debug-Helfer oder ein Block, der nur ausgeführt wird, wenn ein Skript manuell gestartet wird. Mit cover.py können Sie Zeilen mit einem Kommentar ausschließen:
if __name__ == "__main__": # pragma: no cover
main()
Sie können in der Konfiguration auch Ausschlüsse und die zu messenden Quellordner festlegen, beispielsweise in pyproject.toml:
[tool.coverage.run]
branch = true
source = ["shop"]
[tool.coverage.report]
show_missing = true
exclude_also = ["if TYPE_CHECKING:"]
Gehen Sie mit Ausschlüssen sparsam um und überprüfen Sie sie wie Code. Jede davon ist eine Entscheidung, bei der einige Zeilen niemals überprüft werden. Der Ausschluss eines schwer zu testenden Fehlerbehandlers, nur um die Zahl zu erhöhen, verfehlt den Zweck.
Für die tägliche Arbeit ist der HTML-Bericht nützlicher als die Terminaltabelle. Führen Sie coverage html aus und öffnen Sie htmlcov/index.html. Für jede Datei werden ausgeführte Zeilen in Grün, fehlende Zeilen in Rot und Teilverzweigungen in Gelb angezeigt, sodass Sie auf einen Blick erkennen können, welche Datei if nie ihren anderen Weg eingeschlagen hat. Viele Editoren können die Abdeckung auch am Rand anzeigen, wodurch Lücken bereits beim Schreiben des Codes und nicht erst danach sichtbar werden.
Abdeckung vor dem Refactoring
Die Abdeckung ist auch eine Sicherheitsüberprüfung vor dem Refactoring. Wenn Sie planen, ein Modul neu zu strukturieren, sehen Sie sich zunächst dessen Bericht an. In den roten Bereichen gibt es keine Tests, die Sie bei Verhaltensänderungen warnen. Fügen Sie daher Charakterisierungstests hinzu, die das aktuelle Verhalten erfassen, bevor Sie beginnen. Auf diese Weise wird die Abdeckung nicht mehr zum Ziel, sondern zu einem praktischen Planungsinstrument.
Derselbe Bericht hilft, wenn ein Fehler in die Produktion gelangt. Suchen Sie die Zeile, in der der Fehler aufgetreten ist, und prüfen Sie, ob er bei einem Test ausgeführt wurde. Wenn nicht, haben Sie eine Lücke gefunden, die es wert ist, geschlossen zu werden. Wenn es bei einem Test ausgeführt wurde, liegt das Problem in einer schwachen Behauptung, was genau die Situation ist, die unser No-Assert-Beispiel gezeigt hat. In beiden Antworten erfahren Sie, wie Sie die Suite verbessern können.
Fazit
Die Codeabdeckung ist eine Taschenlampe, keine Note. Verwenden Sie die Zweigabdeckung, um Code zu finden, der von keinem Test berührt wird, insbesondere die Fehlerbehandlung, und schreiben Sie dann Tests, die echte Aussagen treffen. Wenn Sie sich an ein Ergebnis unseres Laufs erinnern, nennen Sie es dieses: Eine 100-prozentige Abdeckung wurde durch einen Test erreicht, bei dem nichts überprüft wurde.
Häufige Fragen
Was ist Codeabdeckung beim Testen?
Dabei handelt es sich um den Prozentsatz des Codes, der während der Ausführung automatisierter Tests ausgeführt wird, gemessen anhand von Zeilen, Verzweigungen oder Bedingungen. Es zeigt, welche Tests berührt wurden, nicht, ob sie es überprüft haben.
Was ist der Unterschied zwischen Leitungs- und Filialabdeckung?
Bei der Leitungsabdeckung wird überprüft, ob jede Leitung ausgeführt wurde. Die Filialabdeckung prüft, ob jede Entscheidung in beide Richtungen ging. Branch Coverage findet ungetestete Pfade, die der Line Coverage entgehen.
Ist eine 100-prozentige Codeabdeckung gut?
Nicht von alleine. In unserem Test erreichte eine Suite ohne Zusicherungen 100 %. Streben Sie nach aussagekräftigen Tests für wichtigen Code und nicht nach einer perfekten Zahl.
Was ist ein guter Prozentsatz der Codeabdeckung?
Viele Teams streben insgesamt etwa 70 bis 85 % an, wobei mehr auf kritische Logik gesetzt wird. Der Trend und die Qualität der Aussagen sind wichtiger als die genaue Anzahl.
Wie unterscheiden sich Mutationstests von der Berichterstattung?
Mutationstests verändern Ihren Code geringfügig und prüfen, ob ein Test fehlschlägt. Es misst, ob Tests Fehler erkennen, was durch die Abdeckung nicht möglich ist.
Lerne weiter
Benötigen Sie Hilfe beim Testen, WordPress oder SEO?
Wir helfen Teams, bessere Software zu liefern und ihre Websites zu erweitern. Sagen Sie uns, woran Sie gerade arbeiten, und wir schlagen Ihnen einen klaren nächsten Schritt vor.
Weitere Anleitungen finden Sie unter Tests, KI und moderne Entwicklung
Hat Ihnen dieser Artikel gefallen? Wählen Sie ein Thema und springen Sie direkt in unsere neuesten Tutorials.
KI-Halluzinationen erklärt: Warum Sprachmodelle Dinge erfinden und wie man sie reduziert
KI-Halluzinationen erklärt: Was sie sind, warum Sprachmodelle sie produzieren, gängige Typen, wie man sie mit Erdung und Eingabeaufforderungen reduz…
Artikel lesen →DevOpsErklärte Umgebungsvariablen: .env-Dateien, Geheimnisse und Konfiguration richtig gemacht
Umgebungsvariablen anhand von Python-Beispielen erklärt, die wir ausgeführt haben: Sie lesen und festlegen, warum jeder Wert ein String ist, .env-Da…
Artikel lesen →SoftwareentwicklungAPI-Ratenbegrenzung erklärt: Algorithmen, Header und Behandlung von 429-Fehlern
API-Ratenbegrenzung anhand einer von uns durchgeführten Simulation erklärt: Token-Bucket, festes Fenster, gleitendes Fenster, 429 Antworten, Ratenbe…
Artikel lesen →TestautomatisierungSpott in Python: unittest.mock, Patch und die Fehler, die Tests zum Lügen machen
Mocking in Python mit unittest.mock, getestet: Mock und return_value, side_effect, Patchen, wo ein Name verwendet wird, Spezifikation und Autospec, …
Artikel lesen →SoftwaretestsCode-Abdeckung erklärt: Leitungs- und Zweigstellenabdeckung und was 100 % wirklich bedeutet
Erklärung der Codeabdeckung anhand eines echten Coverage.py-Laufs: Zeilen-, Zweig- und Bedingungsabdeckung, Lesen eines Berichts, warum bei 100 % Ab…
Artikel lesen →Datenbanken & SQLDELETE vs. TRUNCATE vs. DROP in SQL: Unterschiede, Rollback und Verwendungszweck
DELETE vs. TRUNCATE vs. DROP erklärt mit getestetem SQL: was jeweils entfernt wird, Rollback, Trigger, Identitätsrücksetzung, Geschwindigkeit und wi…
Artikel lesen →Datenbanken & SQLErklärte SQL-Ansichten: Erstellen, nutzen und vermeiden Sie Fallstricke mit Beispielen
SQL-Ansichten anhand der von uns ausgeführten Abfragen erklärt: Erstellen einer Ansicht, Live-Ergebnisse, Ausblenden von Spalten, Leistung, aktualis…
Artikel lesen →SoftwareentwicklungTypeScript-Schnittstelle vs. Typ: Echte Unterschiede, getestet mit dem Compiler
TypeScript-Schnittstelle vs. Typ, überprüft mit dem echten Compiler: Was beide können, was nur einer kann, Deklarationszusammenführung, Erweiterunge…
Artikel lesen →SoftwareentwicklungPython *args und **kwargs erklärt: Flexible Funktionen mit klaren Beispielen
Python *args und **kwargs erklärt mit Code, den wir ausgeführt haben: Packen und Entpacken, Parameterreihenfolge, Nur-Schlüsselwort- und Nur-Positio…
Artikel lesen →SoftwareentwicklungPython-Lambda-Funktionen: Syntax, tatsächliche Verwendung und wann man sie nicht verwendet
Python-Lambda-Funktionen erklärt mit Code, den wir ausgeführt haben: Syntax, Sortierung mit Schlüssel, Karte und Filter, Abschlüsse, die Late-Bindin…
Artikel lesen →Künstliche IntelligenzWas sind KI-Agenten? Wie sie funktionieren, mit einer einfachen Agentenschleife, die Sie ausführen können
Was sind KI-Agenten? Eine einfache Anleitung zur Agentenschleife, zu Tools, Speicher und Leitplanken, mit einer kleinen Python-Schleife, die wir aus…
Artikel lesen →DevOpsÜberwachung vs. Beobachtbarkeit: Der wahre Unterschied, mit Protokollen, Metriken und Traces
Überwachung vs. Beobachtbarkeit erklärt: bekannte und unbekannte Fehler, Protokolle, Metriken und Spuren, eine exemplarische Vorgehensweise für Chec…
Artikel lesen →DevOpsDocker-Volumes vs. Bind Mounts: Containerdaten auf die richtige Art und Weise beibehalten
Docker-Volumes vs. Bind-Mounts erklärt: Warum Containerdaten verschwinden, benannte Volumes, Bind-Mounts, tmpfs, Compose-Beispiele, Backups, Berecht…
Artikel lesen →TestautomatisierungTDD vs. BDD: Hauptunterschiede, eine Funktion, die auf beide Arten entwickelt wurde und wann sie jeweils verwendet werden sollte
TDD vs. BDD erklärt mit einer Rabattfunktion, die in beide Richtungen aufgebaut ist: Rot-Grün-Refaktor mit Code, den wir ausgeführt haben, Gegeben-W…
Artikel lesen →SoftwareentwicklungGit merge vs rebase: Unterschiede, Beispiele und wann man was nutzt
Git merge vs rebase erklärt mit echter Log-Ausgabe, einer Vergleichstabelle, der goldenen Regel des Rebase und einem sicheren Team-Workflow.
Artikel lesen →SoftwareentwicklungPython List Comprehension: Syntax, Beispiele und wann man darauf verzichtet
Python List Comprehension erklärt: Syntax, Filter, if-else, verschachtelte Schleifen, gemessene Zeiten und Fälle, in denen eine normale for-Schleife…
Artikel lesen →Datenbanken & SQLSQL Joins erklärt mit Beispielen (Inner, Left, Right, Full)
SQL Joins Schritt für Schritt mit Beispieltabellen und Abfragen verstehen: INNER, LEFT, RIGHT, FULL, CROSS und SELF, plus typische Fehler und Perfor…
Artikel lesen →