Einleitung: Warum Testen in Rust nicht nur modisch, sondern notwendig ist
\n\nRust ist eine Sprache, die für ihre Speichersicherheit und hohe Leistung bekannt ist. Doch selbst der strengste Compiler garantiert nicht die Korrektheit der Geschäftslogik. Hier kommt das Testen ins Spiel. In Rust eliminiert das Typ- und Eigentumssystem bereits zur Kompilierzeit eine ganze Klasse von Fehlern, aber Modultests, Integrationstests und End-to-End (E2E)-Tests stellen sicher, dass Ihr Code genau das tut, was Sie beabsichtigen.
\n\nIn diesem Artikel betrachten wir drei Testebenen in Rust: von isolierten Unit-Tests bis hin zu vollständigen E2E-Szenarien. Sie erfahren, wie Sie effektive Tests schreiben, welche Werkzeuge Sie verwenden und wie Sie die Testinfrastruktur organisieren. Das Material ist sowohl für Anfänger, die gerade mit Rust beginnen, als auch für erfahrene Entwickler geeignet, die ihr Wissen systematisieren möchten.
\n\n1. Unit-Tests: Die Grundlage
\n\nUnit-Tests (Modultests) sind die schnellste und isolierteste Prüfebene. Sie testen einzelne Funktionen, Methoden oder kleine Module unabhängig von externen Abhängigkeiten. In Rust werden Unit-Tests üblicherweise direkt in derselben Datei wie der zu testende Code innerhalb des Moduls tests platziert.
1.1. Struktur und Attribute
\n\nZum Erstellen eines Unit-Tests wird das Attribut #[test] verwendet. Die damit markierte Funktion akzeptiert keine Argumente und gibt nichts zurück (oder gibt ein Result zurück). Wenn innerhalb der Funktion ein Panic auftritt, gilt der Test als fehlgeschlagen.
// src/lib.rs\npub fn add(a: i32, b: i32) -> i32 {\n a + b\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn test_add() {\n assert_eq!(add(2, 2), 4);\n }\n\n #[test]\n fn test_add_negative() {\n assert_eq!(add(-1, -1), -2);\n }\n}\n\nBeachten Sie #[cfg(test)]. Dieses Attribut stellt sicher, dass das Modul mit den Tests nur beim Ausführen der Tests (cargo test) kompiliert wird. In den Produktionsbuild gelangt es nicht.
1.2. Makros für Überprüfungen
\n\nRust bietet mehrere Makros zur Überprüfung von Behauptungen:
\n\n- \n
assert!(expr)— prüft, ob ein Ausdruck wahr ist. \n assert_eq!(left, right)— prüft auf Gleichheit (erfordertPartialEqundDebug). \n assert_ne!(left, right)— prüft auf Ungleichheit. \n panic!("message")— löst erzwungenermaßen einen Panic aus (wird zum Testen von Fehlern verwendet). \n
1.3. Testen von Panics mit should_panic
\n\nWenn Ihre Funktion bei ungültigen Eingaben einen Panic auslösen soll, verwenden Sie das Attribut #[should_panic]:
pub fn divide(a: f64, b: f64) -> f64 {\n if b == 0.0 {\n panic!("Division by zero");\n }\n a / b\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n #[should_panic(expected = "Division by zero")]\n fn test_divide_by_zero() {\n divide(10.0, 0.0);\n }\n}\n\nDer Parameter expected ist nicht zwingend erforderlich, macht den Test jedoch präziser: Der Panic muss die angegebene Zeichenfolge enthalten.
2. Integrationstests: Überprüfung der Modulinteraktion
\n\nIntegrationstests prüfen, wie verschiedene Teile Ihrer Anwendung zusammenarbeiten. Im Gegensatz zu Unit-Tests befinden sie sich in einem separaten Verzeichnis tests/ auf der Projektebene (neben src/). Jede Datei in diesem Ordner wird als separate Crate-Bibliothek kompiliert.
2.1. Erstellen eines Integrationstests
\n\nErstellen Sie eine Datei tests/integration_test.rs:
// tests/integration_test.rs\nuse my_crate::add; // Angenommen, Ihre Crate heißt my_crate\n\n#[test]\nfn test_add_integration() {\n let result = add(5, 10);\n assert_eq!(result, 15);\n}\n\nUm nur die Integrationstests auszuführen