Softwarequalität und Testabdeckung
Testabdeckung (Code Coverage) ist eine der wichtigsten Metriken in der Softwarequalität. Sie misst, wie viel Prozent des Codes durch automatisierte Tests ausgeführt werden. Doch hohe Testabdeckung allein garantiert keine hohe Qualität — es kommt darauf an, was und wie gemessen wird.
In a Nutshell
Testabdeckung quantifiziert den Anteil des Codes, der durch Tests ausgeführt wird. Die wichtigsten Metriken sind Line Coverage, Branch Coverage und Function Coverage. Ein Coverage-Wert von 80% bedeutet: 80% der Zeilen wurden während der Testausführung mindestens einmal erreicht. Das sagt aber nichts darüber aus, ob die Tests auch die richtigen Assertions enthalten.
Kompakte Fachbeschreibung
Code Coverage ist ein Maß für den Grad, in dem der Quellcode eines Programms durch Tests ausgeführt wird. Sie wird als Prozentsatz ausgedrückt und kann auf verschiedenen Ebenen gemessen werden: Zeilen, Anweisungen, Zweigen (Branches), Funktionen und Pfaden. Coverage-Tools instrumentieren den Code während der Ausführung und zeichnen auf, welche Code-Bereiche durchlaufen wurden. Die Metrik hilft, ungetestete Bereiche zu identifizieren, ist aber keine Garantie für Fehlerfreiheit — ein Test kann eine Zeile ausführen, ohne das erwartete Verhalten zu verifizieren.
Wer braucht Testabdeckung und warum?
Testabdeckung ist relevant für:
- Entwickler: um zu sehen, welche Code-Bereiche noch ungetestet sind
- Teams: um eine Mindestqualitätsschwelle zu definieren
- QA-Engineers: um Risiko-Bereiche zu identifizieren
- Projektmanager: um den Reifegrad der Teststrategie zu bewerten
- Compliance: in regulierten Branchen (Medizin, Automotive, Finanzwesen) sind Coverage-Schwellen oft vorgeschrieben
Coverage-Metriken im Detail
Line Coverage (Zeilenabdeckung)
Misst, wie viele Zeilen des Quellcodes durch Tests ausgeführt wurden. Die einfachste und am häufigsten verwendete Metrik.
function classify(score) {
if (score >= 90) return 'A'; // Zeile 1
if (score >= 80) return 'B'; // Zeile 2
if (score >= 70) return 'C'; // Zeile 3
return 'F'; // Zeile 4
}
Ein Test mit classify(95) deckt nur Zeile 1 ab → 25% Line Coverage. Tests mit classify(95) und classify(65) decken Zeile 1 und 4 ab → 50%.
Branch Coverage (Zweigabdeckung)
Misst, wie viele Verzweigungen (if/else, switch, ternary) durch Tests abgedeckt wurden. Jede Verzweigung hat zwei Zweige: true und false.
function classify(score) {
if (score >= 90) return 'A'; // Branch 1: true/false
if (score >= 80) return 'B'; // Branch 2: true/false
if (score >= 70) return 'C'; // Branch 3: true/false
return 'F';
}
6 Branches insgesamt (3× true, 3× false). Ein Test mit classify(95) deckt Branch 1-true ab, aber nicht Branch 1-false → 1/6 = 17% Branch Coverage.
Function Coverage (Funktionsabdeckung)
Misst, wie viele Funktionen im Code mindestens einmal aufgerufen wurden. Besonders wichtig für Module mit vielen kleinen Funktionen.
Path Coverage (Pfadabdeckung)
Misst, wie viele möglichen Ausführungspfade durch den Code abgedeckt wurden. Die strengste Metrik, da sie alle Kombinationen von Verzweigungen betrachtet. Bei n unabhängigen Verzweigungen gibt es 2^n Pfade — Path Coverage ist bei komplexem Code oft unrealistisch.
Statement Coverage (Anweisungsabdeckung)
Ähnlich wie Line Coverage, bezieht sich aber auf einzelne Anweisungen statt Zeilen. Eine Zeile kann mehrere Anweisungen enthalten.
Praxisbeispiel: Jest mit Istanbul Coverage
Setup
// package.json
{
"scripts": {
"test": "jest",
"test:coverage": "jest --coverage"
},
"jest": {
"collectCoverageFrom": [
"src/**/*.js",
"!src/**/*.spec.js",
"!src/index.js"
],
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}
Beispiel-Modul
// src/utils/discount.js
export function calculateDiscount(price, customerType) {
if (typeof price !== 'number' || price < 0) {
throw new Error('Price must be a non-negative number');
}
switch (customerType) {
case 'premium':
return price * 0.8; // 20% Rabatt
case 'vip':
return price * 0.7; // 30% Rabatt
case 'standard':
return price; // Kein Rabatt
default:
throw new Error(`Unknown customer type: ${customerType}`);
}
}
Tests mit vollständiger Branch Coverage
// src/utils/discount.spec.js
import { calculateDiscount } from './discount.js';
describe('calculateDiscount', () => {
test('premium customer gets 20% discount', () => {
expect(calculateDiscount(100, 'premium')).toBe(80);
});
test('vip customer gets 30% discount', () => {
expect(calculateDiscount(100, 'vip')).toBe(70);
});
test('standard customer gets no discount', () => {
expect(calculateDiscount(100, 'standard')).toBe(100);
});
test('throws on negative price', () => {
expect(() => calculateDiscount(-1, 'premium'))
.toThrow('Price must be a non-negative number');
});
test('throws on non-number price', () => {
expect(() => calculateDiscount('100', 'premium'))
.toThrow('Price must be a non-negative number');
});
test('throws on unknown customer type', () => {
expect(() => calculateDiscount(100, 'unknown'))
.toThrow('Unknown customer type: unknown');
});
});
Coverage-Report
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
discount | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Coverage-Ziele: Welche Werte sind sinnvoll?
| Projekttyp | Empfohlene Coverage | Begründung |
|---|---|---|
| Bibliothek / SDK | 90-100% | Hohe Stabilität, viele Konsumenten |
| Web-Anwendung | 70-80% | Pragmatisch, Fokus auf Geschäftslogik |
| Prototyp / MVP | 40-60% | Schnelle Iteration, Tests für Kernlogik |
| Regulierte Software | 100% Branch | Zwingend erforderlich (IEC 62304, DO-178C) |
| Legacy-Code | 50%+ schrittweise | Erhöhung bei Refactoring |
Faustregel: 80% Line Coverage mit guten Assertions ist wertvoller als 100% Coverage ohne Assertions.
Häufige Fallstricke
1. 100% Coverage ohne Assertions
// Schlecht: Zeile wird ausgeführt, aber nichts geprüft
test('calculateDiscount runs', () => {
calculateDiscount(100, 'premium'); // Keine Assertion!
});
Coverage: 100%, Qualität: 0%.
2. Coverage-Gaming
Entwickler schreiben Tests nur um Coverage-Schwellen zu erreichen, ohne sinnvolle Assertions. Abhilfe: Mutation Testing einsetzen.
3. Ignore-Pragmas übermäßig verwenden
/* istanbul ignore next */
export function complexFunction() { ... }
Jedes ignore versteckt ungetesteten Code. Verwende es nur für plattform-spezifischen Code oder bewusst ausgeschlossene Fehlerpfade.
4. Nur Line Coverage messen
Line Coverage ist die schwächste Metrik. Branch Coverage findet ungetestete Edge-Cases, die Line Coverage übersieht.
Mutation Testing: Die nächste Stufe
Mutation Testing verändert den Code systematisch (z.B. > zu >=, + zu -) und prüft, ob die Tests den Mutanten abfangen. Wenn ein Mutant überlebt, ist der Test unzureichend.
# Stryker Mutation Testing für JavaScript
npx stryker run
Beispiel-Mutanten:
if (score >= 90)→if (score > 90)— findet Tests, die genau 90 nicht prüfenreturn price * 0.8→return price * 0.9— findet Tests ohne exakte Assertionreturn 'A'→return 'B'— findet Tests ohne Rückgabewert-Prüfung
Tools im Überblick
| Tool | Sprache | Metriken | Besonderheit |
|---|---|---|---|
| Istanbul/nyc | JavaScript | Line, Branch, Func | Standard in Jest |
| Coverage.py | Python | Line, Branch | Standard in pytest |
| JaCoCo | Java | Line, Branch, Method | Standard in Maven/Gradle |
| gcov | C/C++ | Line, Branch | GCC-integriert |
| Stryker | JS/TS | Mutation Score | Mutation Testing |
| PIT | Java | Mutation Score | Mutation Testing |
Prüfungsrelevante Stichpunkte
- Unterscheidung Line, Branch, Path und Function Coverage
- Coverage ist notwendige, aber nicht hinreichende Bedingung für Qualität
- Coverage-Schwellen in CI/CD-Pipelines als Quality Gate
- Mutation Testing als Ergänzung zur Coverage-Messung
- Coverage-Tools: Istanbul, JaCoCo, Coverage.py, gcov
- 100% Coverage ohne Assertions ist wertlos
- ISO 25010: Testbarkeit als Qualitätsmerkmal
FAQ
1. Line vs Branch Coverage?
2. Ist 100% Coverage genug?
3. Sinnvoller Schwellwert?
4. Was ist Mutation Testing?
5. Was ist Path Coverage?
6. Coverage in Jest konfigurieren?
7. Was ist Istanbul?
8. Coverage in CI/CD erzwingen?
9. Function Coverage?
10. Was ist Coverage-Gaming?
11. Tools für andere Sprachen?
12. Was ist istanbul ignore?
13. Was ist ein LCOV-Report?
14. Coverage in Legacy-Code?
15. Coverage vs Testqualität?
Weiter im Softwarequalität Lernpfad
Der nächste Artikel im Softwarequalität Lernpfad behandelt Clean Code und SOLID Prinzipien — die Grundlagen für lesbaren, wartbaren und erweiterbaren Code.
Quellen
- https://istanbul.js.org/
- https://stryker-mutator.io/
- https://jestjs.io/docs/configuration#coveragethreshold-object
- https://iso25000.com/index.php/en/iso-25000-standards/iso-25010.html
Buchempfehlungen zur Softwarequalität
Wenn Du Dich weiter mit Testabdeckung, Softwarequalität und Testing beschäftigen möchtest, empfehlen wir Dir die folgenden Bücher:
Software Engineering
Bücher über Softwarequalität, Clean Code, Code Reviews und Softwareentwicklungsprozesse
Clean Code: Programmieren in Java – Refactoring, Test-Driven Development und Clean Architecture von Robert C. Martin
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Software Engineering: Umfassendes Handbuch für die Praxis
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Refactoring: Wie Sie bestehenden Code verbessern von Martin Fowler
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.






