Kapitel 18: Testautomatisierung und Dokumentation – Professionelle Test-Strukturen und API-Dokumentation
In der professionellen Software-Architektur sind Tests und Dokumentation keine nachgelagerten Pflichten, sondern integraler Bestandteil des Entwurfs- und CI/CD-Prozesses. Gut strukturierte Test-Suiten ermöglichen angstfreies Refaktorisieren und sichern die Schnittstellen-Stabilität Ihrer Bibliotheken ab. Ausführbare Dokumentationstests garantieren zudem, dass Codebeispiele in der Dokumentation niemals veralten.
1. Lernziele – Das wirst du heute lernen
- Result in Tests nutzen: Sie verwenden den
?-Operator in Testsignaturen zur sauberen Fehlerfortpflanzung. - Fehlerzustände testen (
#[should_panic]): Sie verifizieren erwartete Programmabstürze. - Langsame Tests kontrollieren: Sie schließen rechenintensive Tests über
#[ignore]aus. - Integrationstests aufbauen: Sie trennen Modultests (Unit-Tests) von API-Integrationstests im
tests/-Verzeichnis. - Ausführbare Doc-Tests schreiben: Sie erstellen Dokumentation, die vom Compiler getestet wird.
2. Fortgeschrittene Test-Steuerung
Der ?-Operator in Testfunktionen
Wenn Sie Funktionen testen, die ein Result zurückgeben, müssen Sie nicht mühsam mit unwrap() arbeiten. Sie können Ihre Testfunktion so konfigurieren, dass sie selbst ein Result zurückgibt:
#![allow(unused)]
fn main() {
fn parsiere_port(s: &str) -> Result<u16, std::num::ParseIntError> {
s.trim().parse::<u16>()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parsiere_port() -> Result<(), std::num::ParseIntError> {
let port = parsiere_port(" 8080 ")?; // Schlägt fehl, falls ein ParseError fliegt
assert_eq!(port, 8080);
Ok(())
}
}
}
Überprüfung auf erwarteten Absturz (#[should_panic])
Wenn Funktionen bei falschen Eingaben abstürzen sollen (z. B. Out-of-Bounds), prüfen wir dies über das Attribut #[should_panic]. Um Fehlalarme durch unbeteiligte Panics zu vermeiden, sollten Sie das erwartete Textfragment spezifizieren:
#![allow(unused)]
fn main() {
pub fn init_datenbank(verbindungen: u32) {
if verbindungen == 0 {
panic!("Datenbank-Pool darf nicht die Groesse 0 haben!");
}
}
#[test]
#[should_panic(expected = "Pool darf nicht die Groesse 0 haben")]
fn test_datenbank_null_panict() {
init_datenbank(0);
}
}
Tests ignorieren (#[ignore])
Tests, die externe Ressourcen benötigen (Netzwerk, Datenbanken) oder sehr langsam sind, markieren Sie mit #[ignore]. Sie werden bei einem standardmäßigen cargo test übersprungen:
#![allow(unused)]
fn main() {
#[test]
#[ignore]
fn schwerer_integrationstest() {
// ...
}
}
Um gezielt nur die ignorierten Tests auszuführen: cargo test -- --ignored.
3. Integrationstests im tests/-Verzeichnis
Unit-Tests liegen direkt in den Quelldateien und dürfen auf private Implementierungsdetails zugreifen. Integrationstests hingegen testen die Bibliothek ausschließlich von außen über die öffentliche API (pub).
Sie liegen in einem separaten Verzeichnis namens tests/ auf der obersten Ebene des Projekts:
mein_projekt/
├── Cargo.toml
├── src/
│ └── lib.rs
└── tests/
└── api_tests.rs
Jede Datei im tests/-Verzeichnis wird vom Compiler als eigenständiges Crate übersetzt. Sie müssen Ihre Bibliothek dort explizit importieren:
#![allow(unused)]
fn main() {
// tests/api_tests.rs
use mein_projekt::multipliziere; // Import des öffentlichen APIs
#[test]
fn test_externe_api() {
assert_eq!(multipliziere(2, 5), 10);
}
}
4. Ausführbare Dokumentationstests (Doc-Tests)
Rust bietet die Möglichkeit, Code-Beispiele in Dokumentationskommentaren automatisch als Tests auszuführen. Das verhindert veraltete und fehlerhafte Dokumentationsbeispiele.
#![allow(unused)]
fn main() {
/// Berechnet den Durchschnitt eines Vektors.
///
/// # Beispiele
///
/// ```
/// let daten = vec![1.0, 2.0, 3.0];
/// let ergebnis = mein_crate::durchschnitt(&daten);
/// assert_eq!(ergebnis, Some(2.0));
/// ```
pub fn durchschnitt(daten: &[f64]) -> Option<f64> {
if daten.is_empty() {
return None;
}
let summe: f64 = daten.iter().sum();
Some(summe / daten.len() as f64)
}
}
Führen Sie cargo test aus, kompiliert der Compiler das Beispiel innerhalb des `/// ````-Blocks und führt es als eigenständigen Test aus.