Einführung
Tests machen aus einer Erwartung einen ausführbaren Nachweis. Ein gezielter Test prüft ein bestimmtes Verhalten, vergleicht das tatsächliche Ergebnis mit dem erwarteten Ergebnis und macht zukünftige Änderungen sicherer, indem er Regressionen automatisch erkennt.
Sie fügen Tests zu einer vorbereiteten Bibliothek für Versandkosten hinzu. Die Funktionen der Produktionsversion sind absichtlich klein gehalten. So können Sie sich darauf konzentrieren, wo Rust-Tests abgelegt werden, wie Assertions Erwartungen ausdrücken, welche Grenzfälle wichtig sind und wie Cargo jeweils nur ein Testziel ausführt.
Einen gezielten Unit-Test hinzufügen
In diesem Schritt legen Sie einen Unit-Test direkt neben dem Bibliothekscode an und führen nur diesen Test anhand seines Namens aus.
Das Projekt befindet sich unter /home/labex/project/shipping-quote. Wechseln Sie in dieses Verzeichnis:
cd /home/labex/project/shipping-quote
Sehen Sie sich vor der Bearbeitung den kurzen Quellcode der Bibliothek an. Der Befehl sed -n '1,220p' dient hier nur zur Anzeige: -n unterdrückt die automatische Ausgabe, und 1,220p gibt ausdrücklich die Zeilen 1 bis 220 aus. Dieser Bereich ist groß genug, um die vollständige Datei anzuzeigen, ohne einen Editor zu öffnen:
sed -n '1,220p' src/lib.rs
Die Schreibweise #[...] bezeichnet ein Attribut: Metadaten, die an das direkt darauf folgende Rust-Element angehängt werden. Das Attribut #[cfg(test)] weist Rust an, das folgende Testmodul nur bei Test-Builds zu kompilieren. Ein Modul ist ein benannter Container für zusammengehörige Elemente. Im nächsten Guided Lab lernen Sie Module als allgemeines Werkzeug zur Organisation kennen.
Innerhalb von mod tests bezeichnet das Pfadwort super das übergeordnete Modul, und * steht für alle aus diesem übergeordneten Modul zugänglichen Namen. Daher macht use super::*; die Bibliotheksfunktionen innerhalb des Testmoduls unter ihren kurzen Namen verfügbar. Eine mit #[test] markierte Funktion wird schließlich zu einem Test, den Cargo erkennen und ausführen kann.
Öffnen Sie den Quellcode mit Nano:
nano src/lib.rs
Ersetzen Sie // FIRST_UNIT_TEST durch diesen kurzen Test:
#[test]
fn light_parcel_costs_five() {
assert_eq!(shipping_cost(3), 5);
}
assert_eq! vergleicht seine beiden Ausdrücke. Sind sie gleich, wird der Test fortgesetzt. Andernfalls schlägt der Test fehl und gibt beide Werte aus. Dieser Test hält fest, dass der Versand eines Pakets mit drei Kilogramm fünf Einheiten kostet.
Speichern Sie mit Ctrl+O, drücken Sie Enter und beenden Sie Nano mit Ctrl+X. Geben Sie den Testnamen nach cargo test an, um nur Tests auszuführen, deren Name diesen Text enthält:
cargo test light_parcel_costs_five
Cargo kompiliert den Test-Build und meldet den benannten Unit-Test:
test tests::light_parcel_costs_five ... ok
test result: ok. 1 passed; 0 failed; ...
Das Präfix tests:: zeigt, dass sich der Test im lokalen Testmodul der Bibliothek befindet. ok bestätigt, dass seine Assertion erfolgreich war.
Grenzfälle abdecken
In diesem Schritt fügen Sie Tests für die Grenzen hinzu, an denen sich das Versandverhalten ändert.
Ein repräsentativer Wert wie 3 bestätigt einen Punkt innerhalb des Bereichs für leichte Pakete. Fehler verstecken sich jedoch häufig an den Grenzen. Im vorbereiteten match ist 1..=5 ein Bereichsmuster, das jeden Wert von eins bis fünf einschließlich beider Endpunkte abgleicht. Es verwendet die Schreibweise für inklusive Bereiche aus Schleifen nun in einem neuen Musterkontext. Die Funktion behandelt den Wert null besonders und behält den Tarif von fünf Einheiten bis einschließlich fünf Kilogramm bei. Durch Tests dieser Werte halten Sie beide Grenzen fest.
Öffnen Sie die Bibliothek erneut:
nano src/lib.rs
Ersetzen Sie // EDGE_UNIT_TESTS durch zwei kurze Tests:
#[test]
fn empty_parcel_is_free() {
assert_eq!(shipping_cost(0), 0);
}
#[test]
fn five_kilograms_is_still_light() {
assert_eq!(shipping_cost(5), 5);
}
Jeder Test beschreibt in seinem Namen eine Regel und enthält eine einzelne gezielte Assertion. Dadurch lässt sich ein Fehler leicht zuordnen. Speichern Sie die Datei und beenden Sie Nano.
Der Filter tests:: gleicht die vollständigen Namen der Tests im lokalen Unit-Testmodul ab. Verwenden Sie ihn, um alle drei Unit-Tests auszuführen und das separate Integrationstestziel vorerst auszuschließen:
cargo test tests::
Der entscheidende Nachweis besteht aus drei erfolgreichen Unit-Tests:
test tests::empty_parcel_is_free ... ok
test tests::five_kilograms_is_still_light ... ok
test tests::light_parcel_costs_five ... ok
test result: ok. 3 passed; 0 failed; ...
Zusammen decken die Beispiele einen gewöhnlichen Wert sowie die beiden wichtigen Grenzen der Regel für leichte Pakete ab.
Einen Integrationstest vervollständigen
In diesem Schritt testen Sie die Bibliothek über ihre öffentliche Schnittstelle aus einer separaten Datei.
Unit-Tests innerhalb von src/lib.rs liegen nahe an der Implementierung und können auf interne Elemente des übergeordneten Moduls zugreifen. Integrationstests befinden sich im Verzeichnis tests/ auf oberster Ebene, werden als separate Testprogramme kompiliert und verwenden wie ein externer Aufrufer nur die öffentliche Schnittstelle der Bibliothek. Der Paketname shipping-quote wird im Quellcode zum Rust-Crate-Namen shipping_quote, also mit einem Unterstrich. Im nächsten Lab werden die Begriffe Paket, Crate, Modul und öffentliche Sichtbarkeit zusammengeführt.
Sehen Sie sich das vorbereitete Gerüst für den Integrationstest an:
sed -n '1,160p' tests/order_workflow.rs
Es importiert nur die öffentliche Funktion order_total, ruft sie für eine Bestellung im Wert von 40 Einheiten und ein Paket mit drei Kilogramm auf und lässt eine Assertion zur Vervollständigung offen. Öffnen Sie die Datei:
nano tests/order_workflow.rs
Ersetzen Sie die Platzhalterzeile für die Assertion, die mit // INTEGRATION_ASSERTION endet, durch:
assert_eq!(total, 45);
Die erwartete Gesamtsumme setzt sich aus dem Zwischenbetrag von 40 Einheiten und den Versandkosten von fünf Einheiten für ein leichtes Paket zusammen. Speichern Sie die Datei und beenden Sie Nano.
Mit der Option --test order_workflow wählen Sie das Integrationstestziel aus, dessen Datei tests/order_workflow.rs ist:
cargo test --test order_workflow
Das Testziel meldet seinen Test für den öffentlichen Ablauf:
test order_total_includes_shipping ... ok
test result: ok. 1 passed; 0 failed; ...
Führen Sie abschließend ohne Filter die gesamte Testsuite des Pakets aus:
cargo test
Cargo führt die drei Unit-Tests und den Integrationstest aus. Separate Ergebniszusammenfassungen sind normal, weil es sich um unterschiedliche Test-Binärdateien handelt. Jeder benannte Test sollte ok anzeigen, und jede Zusammenfassung sollte null Fehler ausweisen.
Zusammenfassung
Sie haben lokale Unit-Tests hinzugefügt, die Grenzen einer Regel abgedeckt, einen Integrationstest über die öffentliche API der Bibliothek vervollständigt und Cargo-Filter verwendet, um einen einzelnen Test, ein Modul, ein Integrationstestziel oder die vollständige Testsuite auszuführen. Diese Vorgehensweisen liefern während der Entwicklung schnell Nachweise und bieten vor der Übergabe einen umfassenderen Schutz vor Regressionen.


