Skip to content
Lektion 60 von 1150/115 abgeschlossen
Modul 15 — Automatisiertes Testen

Warum testen & cargo test

Tests als Sicherheitsnetz

Ein Test ist eine Funktion, die prüft, dass sich dein Code so verhält, wie du es erwartest. Tests fangen Fehler bevor sie deine Nutzer erreichen — und sie erlauben dir, mutig umzubauen, ohne etwas kaputt zu machen.

Rust bringt das Test-Framework direkt mit: cargo test kompiliert dein Projekt und führt alle Testfunktionen aus.

Dein erster Test

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}
  • #[test] markiert eine Funktion als Test
  • #[cfg(test)] sagt: kompiliere dieses Modul nur bei cargo test
  • assert_eq! prüft auf Gleichheit; schlägt fehl, wenn nicht

cargo test meldet am Ende die Anzahl bestandener und fehlgeschlagener Tests.

cfg(test) hält Tests aus dem Build

#[cfg(test)] sorgt dafür, dass Test-Code nur beim Testen kompiliert wird — nicht in deinem normalen cargo build oder cargo run. So blähen deine Tests die ausgelieferte Binary nicht auf.

📝 Schnellprüfung

Was macht #[cfg(test)]?

✅ Wichtige Erkenntnisse

Haken setzen, um deinen Lernfortschritt zu markieren:

Lektion 61 von 1150/115 abgeschlossen
Modul 15 — Automatisiertes Testen

Assertions: assert!, assert_eq! & Co.

Die Assertions-Makros

  • assert!(bedingung) — schlägt fehl, wenn die Bedingung falsch ist
  • assert_eq!(links, rechts) — prüft Gleichheit; zeigt beide Werte beim Fehler
  • assert_ne!(links, rechts) — prüft Ungleichheit
  • panic!(...) — bricht sofort ab (Test schlägt fehl)
#[test]
fn test_assertions() {
    assert!(1 + 1 == 2);
    assert_eq!(2 * 3, 6);
    assert_ne!(5, 6);
}

Fehlerdiagnose verbessern

Bei assert_eq! zeigt Rust bei einem Fehler beide Werte. Für eigenen Text hängt man eine Nachricht an:

#[test]
fn mit_nachricht() {
    let wert = 3;
    assert_eq!(wert, 4, "Wert {} war nicht 4", wert);
}

Warum assert_eq! die Debug-Ausgabe braucht

assert_eq! und assert_ne! benötigen, dass die Typen das Debug- und PartialEq-Trait implementieren. Die meisten Standardtypen tun das bereits; für eigene Structs leitest du sie mit #[derive(Debug, PartialEq)] ab.

📝 Schnellprüfung

Was zeigt assert_eq! bei einem Fehler an?

✅ Wichtige Erkenntnisse

Haken setzen, um deinen Lernfortschritt zu markieren:

Lektion 62 von 1150/115 abgeschlossen
Modul 15 — Automatisiertes Testen

should_panic & Result in Tests

Erwartete Panics testen

Manchmal soll Code bewusst panicken (z. B. bei ungültiger Eingabe). Mit #[should_panic] markierst du einen Test, der nur besteht, wenn er panickt:

#[test]
#[should_panic(expected = "leer")]
fn panic_bei_leerem_input() {
    let v: Vec<i32> = vec![];
    if v.is_empty() {
        panic!("Liste ist leer");
    }
    let _ = v[0];
}

expected prüft, dass die Panic-Nachricht den Text enthält — so fängst du versehentliche Panics aus anderen Gründen ab.

Tests, die Result zurückgeben

Ein Test kann auch ein Result zurückgeben. Dann gilt: Ok(()) = bestanden, Err(_) = fehlgeschlagen. Damit kannst du ? in Tests nutzen:

#[test]
fn mit_result() -> Result<(), String> {
    let s = "42".parse::<i32>();
    s.map(|_| ()).map_err(|e| format!("{e}"))
}

should_panic erfasst nur die erwartete Panic

#[should_panic(expected = "...")] stellt sicher, dass die Panic wirklich von der erwarteten Stelle kommt. Ohne expected bestünde der Test auch, wenn irgendwo anders — etwa in der Test-Vorbereitung — ein Panic auftritt.

📝 Schnellprüfung

Wann besteht ein Test mit #[should_panic]?

✅ Wichtige Erkenntnisse

Haken setzen, um deinen Lernfortschritt zu markieren:

Lektion 63 von 1150/115 abgeschlossen
Modul 15 — Automatisiertes Testen

Unit-Tests: nah am Code

Unit-Tests: klein und isoliert

Unit-Tests prüfen einzelne Funktionen oder Module isoliert — ohne Nebenwirkungen wie Dateien oder Netzwerk. Konventionell stehen sie in derselben Datei wie der Code, in einem #[cfg(test)]-Modul:

pub fn addiere(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*; // bringt addiere in den Scope

    #[test]
    fn test_addiere() {
        assert_eq!(addiere(2, 3), 5);
    }
}

Zugriff auf private Items

Weil das Test-Modul innerhalb der Datei liegt, kann es auch private Funktionen und Felder testen — ein Vorteil der Unit-Tests in derselben Datei.

use super::* bringt alles nach oben

use super::*; importiert alle Items des Elternmoduls (die Datei, in der der Test steht) in den Test-Scope. So kannst du addiere(2, 3) direkt aufrufen, statt super::addiere(2, 3) zu schreiben.

📝 Schnellprüfung

Wo stehen Unit-Tests konventionell?

✅ Wichtige Erkenntnisse

Haken setzen, um deinen Lernfortschritt zu markieren:

Lektion 64 von 1150/115 abgeschlossen
Modul 15 — Automatisiertes Testen

Integrationstests: die öffentliche API

Integrationstests: von außen

Integrationstests prüfen deine Library wie ein externer Nutzer — nur über die öffentliche API. Sie liegen im tests/-Ordner, außerhalb von src/:

projekt/
├── src/
│   └── lib.rs
└── tests/
    └── integration_test.rs
// tests/integration_test.rs
use mein_crate::addiere;

#[test]
fn test_addiere_oeffentlich() {
    assert_eq!(addiere(2, 3), 5);
}

Jede Datei im tests/-Ordner ist eine eigene Crate, die deine Library wie ein Nutzer importiert. Du musst die Funktionen dafür pub machen.

Nur öffentliche API ist erreichbar

Integrationstests sehen nur die pub-Items deiner Library. Das ist Absicht: Sie testen, was auch deine echten Nutzer verwenden. Private Details bleiben unsichtbar — Unit-Tests sind dafür zuständig.

📝 Schnellprüfung

Was können Integrationstests im Gegensatz zu Unit-Tests sehen?

✅ Wichtige Erkenntnisse

Haken setzen, um deinen Lernfortschritt zu markieren:

Lektion 65 von 1150/115 abgeschlossen
Modul 15 — Automatisiertes Testen

Testläufe steuern & organisieren

cargo test steuern

  • cargo test — führt alle Tests aus
  • cargo test name — führt nur Tests aus, deren Name name enthält
  • cargo test -- --test-threads=1 — Tests einzeln (nicht parallel)
  • cargo test -- --nocapture — zeigt println!-Ausgaben der Tests
  • cargo test -- --ignored — führt nur die ignorierten Tests aus

Tests markieren

Mit #[ignore] überspringst du einen Test bei cargo test (nützlich für langsame Tests):

#[test]
#[ignore]
fn sehr_langsam() {
    // teuer, nur bei Bedarf ausführen
}

Mit cargo test -- --ignored führst du nur diese aus.

Test-Threads verstehen

Standardmäßig laufen Tests parallel in mehreren Threads — schneller, aber gemeinsamer Zustand (Dateien, globale Variablen) kann sich in die Quere kommen. Mit --test-threads=1 laufen sie nacheinander, was bei geteilten Ressourcen hilft.

📝 Schnellprüfung

Wie führst du nur Tests aus, deren Name 'foo' enthält?

✅ Wichtige Erkenntnisse

Haken setzen, um deinen Lernfortschritt zu markieren:

Lektion 66 von 1150/115 abgeschlossen
Modul 15 — Automatisiertes Testen

Übung: Eine Funktion absichern

Projekt: Tests für eine kleine Utility-Funktion

Du sicherst eine Funktion mit Unit- und Integrationstests ab:

  1. Schreibe eine Funktion maximum(zahlen: &[i32]) -> Option<i32>, die das größte Element liefert (oder None bei leerem Slice).
  2. Lege ein #[cfg(test)]-Modul mit Unit-Tests an:
    • leeres Slice → None
    • ein Element → dieses Element
    • mehrere Elemente → das größte
  3. Füge einen Test mit #[should_panic] hinzu, der prüft, dass eine absichtlich panickende Hilfsfunktion auch wirklich panickt.
  4. Verschiebe die Funktion in eine Library (lib.rs) und schreibe einen Integrationstest im tests/-Ordner, der maximum über die öffentliche API aufruft.
  5. Führe alles mit cargo test aus und bestätige, dass alle Tests grün sind.

Das verbindet #[test], Assertions, should_panic, Unit- und Integrationstests.

Rezepte aus diesem Modul

  • Test markieren: #[test] in einem #[cfg(test)]-Modul
  • Prüfen: assert!, assert_eq!, assert_ne!
  • Panic erwarten: #[should_panic(expected = "...")]
  • Von außen testen: tests/-Ordner, eigene Crate
  • Ausführen: cargo test

Übung: maximum() absichern

🧩 Übung m15-l7-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

Haken setzen, um deinen Lernfortschritt zu markieren:

Modul-Checkpoint

Bestehe das Modul mit 80 %. Der Versuch zählt — beantwortete Fragen werden nicht erneut angezeigt; erst nach dem Absenden gibt es Auflösung und Erklärung.

Checkpoint: Modul 15 — Automatisiertes Testen

Bestehensgrenze: 80 %. Der Versuch wird bewertet und zählt für das Modul-Gate.

This site uses essential cookies for Stripe payments. No tracking cookies.