Das Steuern von Testausführungen

RustRustBeginner
Jetzt üben

This tutorial is from open-source community. Access the source code

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

Willkommen zu Controlling How Tests Are Run. Dieser Lab ist ein Teil des Rust Buchs. Du kannst deine Rust-Fähigkeiten in LabEx üben.

In diesem Lab wirst du lernen, wie du das Verhalten von Testläufen in Rust mithilfe von Befehlszeilenoptionen für cargo test und der resultierenden Testbinärdatei steuern kannst.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/DataTypesGroup -.-> rust/integer_types("Integer Types") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") subgraph Lab Skills rust/variable_declarations -.-> lab-100416{{"Das Steuern von Testausführungen"}} rust/integer_types -.-> lab-100416{{"Das Steuern von Testausführungen"}} rust/function_syntax -.-> lab-100416{{"Das Steuern von Testausführungen"}} rust/expressions_statements -.-> lab-100416{{"Das Steuern von Testausführungen"}} end

Controlling How Tests Are Run

Genau wie cargo run deinen Code kompiliert und dann die resultierende Binärdatei ausführt, kompiliert cargo test deinen Code im Testmodus und führt die resultierende Testbinärdatei aus. Das Standardverhalten der von cargo test erzeugten Binärdatei besteht darin, alle Tests parallel auszuführen und die während der Testläufe erzeugte Ausgabe zu erfassen, wodurch die Ausgabe nicht angezeigt wird und es einfacher ist, die Ausgabe in Bezug auf die Testergebnisse zu lesen. Du kannst jedoch Befehlszeilenoptionen angeben, um dieses Standardverhalten zu ändern.

Einige Befehlszeilenoptionen werden an cargo test übergeben, und einige an die resultierende Testbinärdatei. Um diese beiden Arten von Argumenten voneinander zu trennen, listest du die Argumente, die an cargo test übergeben werden, gefolgt vom Separator -- und dann diejenigen, die an die Testbinärdatei übergeben werden. Wenn du cargo test --help ausführst, werden die Optionen angezeigt, die du mit cargo test verwenden kannst, und wenn du cargo test -- --help ausführst, werden die Optionen angezeigt, die du nach dem Separator verwenden kannst.

Tests parallel oder nacheinander ausführen

Wenn du mehrere Tests ausführst, werden diese standardmäßig parallel mithilfe von Threads ausgeführt, was bedeutet, dass sie schneller abgeschlossen werden und du schneller Feedback erhältst. Da die Tests gleichzeitig ausgeführt werden, musst du确保, dass deine Tests nicht voneinander abhängen oder von einem gemeinsamen Zustand abhängen, einschließlich einer gemeinsamen Umgebung, wie dem aktuellen Arbeitsverzeichnis oder Umgebungsvariablen.

Nehmen wir beispielsweise an, dass jeder deiner Tests einige Code ausführt, der eine Datei namens test-output.txt auf der Festplatte erstellt und einige Daten in diese Datei schreibt. Dann liest jeder Test die Daten in dieser Datei und stellt sicher, dass die Datei einen bestimmten Wert enthält, der in jedem Test unterschiedlich ist. Da die Tests gleichzeitig ausgeführt werden, kann ein Test die Datei in der Zeit zwischen dem Schreiben und Lesen der Datei durch einen anderen Test überschreiben. Der zweite Test wird dann fehlschlagen, nicht weil der Code fehlerhaft ist, sondern weil die Tests sich gegenseitig stören, während sie parallel ausgeführt werden. Eine Lösung besteht darin, sicherzustellen, dass jeder Test in eine andere Datei schreibt; eine andere Lösung besteht darin, die Tests nacheinander auszuführen.

Wenn du die Tests nicht parallel ausführen möchtest oder wenn du eine feinere Kontrolle über die Anzahl der verwendeten Threads möchten, kannst du das Flag --test-threads und die Anzahl der Threads, die du verwenden möchtest, an die Testbinärdatei senden. Schau dir das folgende Beispiel an:

cargo test -- --test-threads=1

Wir setzen die Anzahl der Testthreads auf 1, was dem Programm mitteilt, keine Parallelität zu verwenden. Das Ausführen der Tests mit einem Thread wird länger dauern als das parallele Ausführen, aber die Tests werden sich nicht gegenseitig stören, wenn sie sich einen Zustand teilen.

Anzeige der Funktionsausgabe

Standardmäßig fängt die Testbibliothek von Rust alles auf der Standardausgabe ausgegebene an, wenn ein Test erfolgreich ist. Beispielsweise, wenn wir println! in einem Test aufrufen und der Test erfolgreich ist, werden wir die println!-Ausgabe nicht im Terminal sehen; wir sehen nur die Zeile, die angibt, dass der Test bestanden hat. Wenn ein Test fehlschlägt, werden wir alles sehen, was auf die Standardausgabe ausgegeben wurde, zusammen mit der restlichen Fehlermeldung.

Als Beispiel hat Listing 11-10 eine alberne Funktion, die den Wert ihres Parameters ausgibt und 10 zurückgibt, sowie einen Test, der bestanden und einen Test, der fehlschlägt.

Dateiname: src/lib.rs

fn prints_and_returns_10(a: i32) -> i32 {
    println!("I got the value {a}");
    10
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn this_test_will_pass() {
        let value = prints_and_returns_10(4);
        assert_eq!(10, value);
    }

    #[test]
    fn this_test_will_fail() {
        let value = prints_and_returns_10(8);
        assert_eq!(5, value);
    }
}

Listing 11-10: Tests für eine Funktion, die println! aufruft

Wenn wir diese Tests mit cargo test ausführen, sehen wir die folgende Ausgabe:

running 2 tests
test tests::this_test_will_pass... ok
test tests::this_test_will_fail... FAILED

failures:

---- tests::this_test_will_fail stdout ----
1 I got the value 8
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `5`,
 right: `10`', src/lib.rs:19:9
note: run with `RUST_BACKTRACE=1` environment variable to display
a backtrace

failures:
    tests::this_test_will_fail

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0
filtered out; finished in 0.00s

Beachten Sie, dass wir nirgends in dieser Ausgabe I got the value 4 sehen, das ausgegeben wird, wenn der erfolgreich verlaufende Test ausgeführt wird. Diese Ausgabe wurde erfasst. Die Ausgabe des fehlgeschlagenen Tests, I got the value 8 [1], erscheint im Abschnitt der Testzusammenfassungsausgabe, die auch die Ursache des Testfehlschlags zeigt.

Wenn wir auch die ausgegebenen Werte für erfolgreich verlaufende Tests sehen möchten, können wir Rust sagen, die Ausgabe erfolgreicher Tests ebenfalls anzuzeigen, mit --show-output:

cargo test -- --show-output

Wenn wir die Tests in Listing 11-10 erneut mit dem --show-output-Flag ausführen, sehen wir die folgende Ausgabe:

running 2 tests
test tests::this_test_will_pass... ok
test tests::this_test_will_fail... FAILED

successes:

---- tests::this_test_will_pass stdout ----
I got the value 4


successes:
    tests::this_test_will_pass

failures:

---- tests::this_test_will_fail stdout ----
I got the value 8
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `5`,
 right: `10`', src/lib.rs:19:9
note: run with `RUST_BACKTRACE=1` environment variable to display
a backtrace

failures:
    tests::this_test_will_fail

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0
filtered out; finished in 0.00s

Ausführen eines Teils der Tests nach Namen

Manchmal kann das Ausführen eines vollständigen Testpakets lange Zeit in Anspruch nehmen. Wenn du an einem bestimmten Bereich des Codes arbeitest, möchtest du möglicherweise nur die Tests ausführen, die sich auf diesen Code beziehen. Du kannst wählen, welche Tests ausgeführt werden sollen, indem du cargo test den Namen oder die Namen der zu ausführenden Tests als Argument übergibst.

Um zu demonstrieren, wie ein Teil der Tests ausgeführt wird, werden wir zunächst drei Tests für unsere add_two-Funktion erstellen, wie in Listing 11-11 gezeigt, und wählen, welche davon ausgeführt werden sollen.

Dateiname: src/lib.rs

pub fn add_two(a: i32) -> i32 {
    a + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn add_two_and_two() {
        assert_eq!(4, add_two(2));
    }

    #[test]
    fn add_three_and_two() {
        assert_eq!(5, add_two(3));
    }

    #[test]
    fn one_hundred() {
        assert_eq!(102, add_two(100));
    }
}

Listing 11-11: Drei Tests mit drei verschiedenen Namen

Wenn wir die Tests ohne Angabe von Argumenten ausführen, wie wir es zuvor gesehen haben, werden alle Tests parallel ausgeführt:

running 3 tests
test tests::add_three_and_two... ok
test tests::add_two_and_two... ok
test tests::one_hundred... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0
filtered out; finished in 0.00s

Ausführen einzelner Tests

Wir können den Namen einer beliebigen Testfunktion an cargo test übergeben, um nur diesen Test auszuführen:

$ cargo test one_hundred
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.69s
     Running unittests src/lib.rs (target/debug/deps/adder-
92948b65e88960b4)

running 1 test
test tests::one_hundred... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2
filtered out; finished in 0.00s

Nur der Test mit dem Namen one_hundred wurde ausgeführt; die anderen beiden Tests stimmten nicht mit diesem Namen überein. Die Testausgabe lässt uns dadurch wissen, dass wir weitere Tests hatten, die nicht ausgeführt wurden, indem am Ende 2 filtered out angezeigt wird.

Wir können nicht auf diese Weise die Namen mehrerer Tests angeben; nur der erste Wert, der an cargo test gegeben wird, wird verwendet. Es gibt jedoch einen Weg, mehrere Tests auszuführen.

Filtern, um mehrere Tests auszuführen

Wir können einen Teil des Testnamens angeben, und jeder Test, dessen Name diesem Wert entspricht, wird ausgeführt. Beispielsweise können wir die beiden Tests, deren Namen add enthalten, ausführen, indem wir cargo test add ausführen:

$ cargo test add
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-
92948b65e88960b4)

running 2 tests
test tests::add_three_and_two... ok
test tests::add_two_and_two... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1
filtered out; finished in 0.00s

Dieser Befehl führte alle Tests mit add im Namen aus und filterte den Test namens one_hundred aus. Beachten Sie auch, dass das Modul, in dem ein Test erscheint, Teil des Testnamens wird, sodass wir alle Tests in einem Modul ausführen können, indem wir nach dem Modulnamen filtern.

Auslassen einiger Tests, es sei denn, sie werden speziell angefordert

Manchmal können einige bestimmte Tests sehr zeitaufwendig zu executieren sein, sodass Sie sie möglicherweise während der meisten Ausführungen von cargo test ausschließen möchten. Anstatt alle Tests, die Sie tatsächlich ausführen möchten, als Argumente aufzulisten, können Sie stattdessen die zeitaufwendigen Tests mit dem Attribut ignore annotieren, um sie auszuschließen, wie hier gezeigt:

Dateiname: src/lib.rs

#[test]
fn it_works() {
    let result = 2 + 2;
    assert_eq!(result, 4);
}

#[test]
#[ignore]
fn expensive_test() {
    // code that takes an hour to run
}

Nach #[test] fügen wir die Zeile #[ignore] zum Test hinzu, den wir ausschließen möchten. Wenn wir jetzt unsere Tests ausführen, wird it_works ausgeführt, aber expensive_test nicht:

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.60s
     Running unittests src/lib.rs (target/debug/deps/adder-
92948b65e88960b4)

running 2 tests
test expensive_test... ignored
test it_works... ok

test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0
filtered out; finished in 0.00s

Die Funktion expensive_test wird als ignored aufgelistet. Wenn wir nur die ignorierten Tests ausführen möchten, können wir cargo test -- --ignored verwenden:

$ cargo test -- --ignored
    Finished test [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-
92948b65e88960b4)

running 1 test
test expensive_test... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1
filtered out; finished in 0.00s

Indem Sie steuern, welche Tests ausgeführt werden, können Sie sicherstellen, dass die Ergebnisse von cargo test schnell zurückgegeben werden. Wenn Sie an einem Punkt angelangt sind, an dem es Sinn macht, die Ergebnisse der ignored Tests zu überprüfen und Sie die Zeit haben, auf die Ergebnisse zu warten, können Sie stattdessen cargo test -- --ignored ausführen. Wenn Sie alle Tests ausführen möchten, unabhängig davon, ob sie ignoriert werden oder nicht, können Sie cargo test -- --include-ignored ausführen.

Zusammenfassung

Herzlichen Glückwunsch! Sie haben das Lab "Controlling How Tests Are Run" abgeschlossen. Sie können in LabEx weitere Labs absolvieren, um Ihre Fähigkeiten zu verbessern.