Panik in Rust testen: Wie man Tests für Ausnahmesituationen schreibt

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Panik ist kein Todesurteil: Warum Ausnahmesituationen in Rust testen?

\n\n

Stellen Sie sich vor: Sie schreiben in Rust eine Bibliothek für die Arbeit mit Geld. Die Funktion divide_money(amount: u64, parts: u64) soll in Panik geraten, wenn parts gleich Null ist – denn durch Null kann man nicht teilen, und aus architektonischen Gründen wollten Sie weder Option noch Result zurückgeben. Wie können Sie sicherstellen, dass diese Panik tatsächlich eintritt? Oder Sie entwickeln einen Parser, der bei der Übergabe ungültiger Daten abbrechen soll. Ohne Tests auf Panik riskieren Sie "stille" Fehler, die erst in der Produktion auftreten.

\n\n

In Rust ist das Testen von Panik nicht nur eine "nette Option", sondern ein wichtiges Werkzeug zur Erstellung zuverlässigen Codes. In diesem Artikel werden wir untersuchen, wie man Unit-Tests schreibt, die prüfen, ob Ihr Code in den richtigen Situationen tatsächlich in Panik gerät. Sie lernen das Makro #[should_panic], seine Nuancen, Alternativen wie std::panic::catch_unwind und bewährte Verfahren kennen. Los geht's!

\n\n

Der klassische Ansatz: Das Makro #[should_panic]

\n\n

Der einfachste und gebräuchlichste Weg, eine Panik in Rust zu testen, ist die Verwendung des Attributs #[should_panic]. Es wird über die Testfunktion gesetzt und teilt dem Compiler mit: "Dieser Test gilt nur dann als erfolgreich, wenn darin eine Panik aufgetreten ist."

\n\n

Basisbeispiel

\n\n
// lib.rs oder tests/unit_tests.rs\nfn divide(a: i32, b: i32) -> i32 {\n    if b == 0 {\n        panic!("Division durch Null!");\n    }\n    a / b\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    #[should_panic]\n    fn test_divide_by_zero() {\n        divide(10, 0);\n    }\n}
\n\n

Führen Sie die Tests mit dem Befehl cargo test aus – und Sie sehen ein grünes Ergebnis. Tritt keine Panik auf (z. B. wenn Sie die Nullprüfung entfernen), schlägt der Test fehl mit der Meldung: "test test_divide_by_zero ... FAILED (expected to panic)".

\n\n

Prüfung der genauen Panik-Nachricht

\n\n

Manchmal müssen Sie nicht nur die Tatsache einer Panik überprüfen, sondern auch deren spezifische Nachricht. Dies hilft, Fehlalarme zu vermeiden, wenn die Panik aufgrund eines anderen Fehlers auftritt. Verwenden Sie den zusätzlichen Parameter expected:

\n\n
#[test]\n#[should_panic(expected = "Division durch Null!")]\nfn test_divide_by_zero_with_message() {\n    divide(10, 0);\n}\n\n// Dieser Test wird fehlschlagen – die Nachricht ist anders\n#[test]\n#[should_panic(expected = "Division durch Null!")]\nfn test_other_panic() {\n    panic!("Ein ganz anderer Fehler");\n}
\n\n

Wichtig: expected prüft, ob die Panik-Nachricht die angegebene Zeichenfolge enthält (und nicht exakt gleich ist). Dies ist praktisch für lange Nachrichten.

\n\n

Fortgeschrittener Ansatz: std::panic::catch_unwind

\n\n

Manchmal reicht #[should_panic] nicht aus. Zum Beispiel, wenn Sie überprüfen möchten, dass eine Panik an einer bestimmten Stelle und nicht woanders aufgetreten ist, oder wenn Sie asynchronen Code testen müssen. Für solche Fälle gibt es std::panic::catch_unwind – eine Funktion, die eine Panik abfängt und als Result zurückgibt.

\n\n

Wie es funktioniert

\n\n
use std::panic;\n\nfn risky_function(should_panic: bool) {\n    if should_panic {\n        panic!("Notfall!");\n    }\n}\n\n#[test]\nfn test_catch_panic() {\n    let result = panic::catch_unwind(|| {\n        risky_function(true);\n    });\n    assert!(result.is_err()); // Prüft, ob eine Panik aufgetreten ist\n}\n\n#[test]\nfn test_no_panic() {\n    let result = panic::catch_unwind(|| {\n        risky_function(false);\n    });\n    assert!(result.is_ok()); // Keine Panik aufgetreten\n}
\n\n

Prüfung des Panik-Typs

\n\n

Mit catch_unwind können Sie sogar überprüfen, ob die Panik einen bestimmten Wert enthält (nicht nur eine Zeichenfolge):

\n\n
use std::panic;\nuse std::any::Any;\n\nfn panic_with_number() {\n    panic!(42); // Ja, das ist möglich – Panik mit einer Zahl!\n}\n\n#[test]\nfn test_panic_with_number() {\n    let resu                

Blogs

Buchempfehlungen