Einführung
In diesem Lab lernen wir über die Unit-Tests in Rust. Unit-Tests sind Rust-Funktionen, die den nicht-testenden Code über das Durchführen von Einstellungen, Ausführen des Codes und Behaupten der Ergebnisse überprüfen. Diese Tests werden in einem tests-Modul mit dem Attribut #[cfg(test)] geschrieben und mit dem Attribut #[test] markiert. Tests können fehlschlagen, wenn etwas im Testfunktion-Panik auslöst, und Hilfsmakros wie assert!, assert_eq! und assert_ne! werden für die Behauptungen verwendet. Rust 2018 erlaubt es Unit-Tests, Result<()> zurückzugeben, um den ?-Operator für das präzise Testen zu verwenden. Es gibt auch Unterstützung für das Testen von Paniken mit dem Attribut #[should_panic]. Spezifische Tests können mit dem Testnamen mit dem Befehl cargo test ausgeführt werden, und Tests können mit dem Attribut #[ignore] oder durch Ausführen von cargo test -- --ignored ignoriert werden.
Hinweis: Wenn das Lab keinen Dateinamen angibt, können Sie einen beliebigen Dateinamen verwenden. Beispielsweise können Sie
main.rsverwenden, es mitrustc main.rs &&./mainkompilieren und ausführen.
Unit-Tests
Tests sind Rust-Funktionen, die überprüfen, ob der nicht-testende Code wie erwartet funktioniert. Die Körper von Testfunktionen führen normalerweise einige Einstellungen durch, führen den Code aus, den wir testen möchten, und stellen dann sicher, dass die Ergebnisse den Erwartungen entsprechen.
Die meisten Unit-Tests befinden sich in einem tests-Modul mit dem Attribut #[cfg(test)]. Testfunktionen werden mit dem Attribut #[test] markiert.
Tests scheitern, wenn etwas in der Testfunktion einen Panik-Auslöser auslöst. Es gibt einige Hilfsmakros:
assert!(expression)- löst eine Panik aus, wenn der Ausdruckfalseauswertet.assert_eq!(left, right)undassert_ne!(left, right)- testen die linke und rechte Ausdrücke auf Gleichheit und Ungleichheit respective.
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// Dies ist eine wirklich schlechte Additionsfunktion, deren Zweck es ist, in diesem
// Beispiel fehlzuschlagen.
#[allow(dead_code)]
fn bad_add(a: i32, b: i32) -> i32 {
a - b
}
#[cfg(test)]
mod tests {
// Beachten Sie diese nützliche Idiomatik: Importieren von Namen aus dem äußeren (für Mod-Tests) Bereich.
use super::*;
#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
}
#[test]
fn test_bad_add() {
// Dieser Assert würde auslösen und der Test würde fehlschlagen.
// Beachten Sie, dass private Funktionen ebenfalls getestet werden können!
assert_eq!(bad_add(1, 2), 3);
}
}
Tests können mit cargo test ausgeführt werden.
$ cargo test
running 2 tests
test tests::test_bad_add... FAILED
test tests::test_add... ok
failures:
---- tests::test_bad_add stdout ----
thread 'tests::test_bad_add' panicked at 'assertion failed: `(left == right)`
left: `-1`,
right: `3`', src/lib.rs:21:8
note: Run with $(RUST_BACKTRACE=1) for a backtrace.
failures:
tests::test_bad_add
test result: FAILED. 1 passed
1 failed
0 ignored
0 measured
0 filtered out
Tests und ?
Keines der vorherigen Unit-Testbeispiele hatte einen Rückgabetyp. Aber in Rust 2018 können Ihre Unit-Tests Result<()> zurückgeben, was es Ihnen ermöglicht, ? darin zu verwenden! Dies kann sie viel präziser gestalten.
fn sqrt(number: f64) -> Result<f64, String> {
if number >= 0.0 {
Ok(number.powf(0.5))
} else {
Err("negative floats don't have square roots".to_owned())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sqrt() -> Result<(), String> {
let x = 4.0;
assert_eq!(sqrt(x)?.powf(2.0), x);
Ok(())
}
}
Weitere Details finden Sie in der "Edition Guide".
Testen von Paniken
Um Funktionen zu überprüfen, die unter bestimmten Umständen einen Panik-Auslöser auslösen sollten, verwenden Sie das Attribut #[should_panic]. Dieses Attribut akzeptiert einen optionalen Parameter expected = mit dem Text der Panik-Nachricht. Wenn Ihre Funktion auf verschiedene Weise einen Panik-Auslöser auslösen kann, hilft dies sicherzustellen, dass Ihr Test den richtigen Panik-Auslöser testet.
pub fn divide_non_zero_result(a: u32, b: u32) -> u32 {
if b == 0 {
panic!("Divide-by-zero error");
} else if a < b {
panic!("Divide result is zero");
}
a / b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_divide() {
assert_eq!(divide_non_zero_result(10, 2), 5);
}
#[test]
#[should_panic]
fn test_any_panic() {
divide_non_zero_result(1, 0);
}
#[test]
#[should_panic(expected = "Divide result is zero")]
fn test_specific_panic() {
divide_non_zero_result(1, 10);
}
}
Das Ausführen dieser Tests liefert uns Folgendes:
$ cargo test
running 3 tests
test tests::test_any_panic... ok
test tests::test_divide... ok
test tests::test_specific_panic... ok
test result: ok. 3 passed
0 failed
0 ignored
0 measured
0 filtered out
Doc-tests tmp-test-should-panic
running 0 tests
test result: ok. 0 passed
0 failed
0 ignored
0 measured
0 filtered out
Ausführen von spezifischen Tests
Um spezifische Tests auszuführen, kann man den Testnamen an den cargo test-Befehl angeben.
$ cargo test test_any_panic
running 1 test
test tests::test_any_panic... ok
test result: ok. 1 passed
0 failed
0 ignored
0 measured
2 filtered out
Doc-tests tmp-test-should-panic
running 0 tests
test result: ok. 0 passed
0 failed
0 ignored
0 measured
0 filtered out
Um mehrere Tests auszuführen, kann man einen Teil des Testnamens angeben, der mit allen Tests übereinstimmt, die ausgeführt werden sollen.
$ cargo test panic
running 2 tests
test tests::test_any_panic... ok
test tests::test_specific_panic... ok
test result: ok. 2 passed
0 failed
0 ignored
0 measured
1 filtered out
Doc-tests tmp-test-should-panic
running 0 tests
test result: ok. 0 passed
0 failed
0 ignored
0 measured
0 filtered out
Tests ignorieren
Tests können mit dem Attribut #[ignore] markiert werden, um einige Tests auszuschließen. Oder um sie mit dem Befehl cargo test -- --ignored auszuführen
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 2), 4);
}
#[test]
fn test_add_hundred() {
assert_eq!(add(100, 2), 102);
assert_eq!(add(2, 100), 102);
}
#[test]
#[ignore]
fn ignored_test() {
assert_eq!(add(0, 0), 0);
}
}
$ cargo test
running 3 tests
test tests::ignored_test... ignored
test tests::test_add... ok
test tests::test_add_hundred... ok
test result: ok. 2 passed
0 failed
1 ignored
0 measured
0 filtered out
Doc-tests tmp-ignore
running 0 tests
test result: ok. 0 passed
0 failed
0 ignored
0 measured
0 filtered out
$ cargo test -- --ignored
running 1 test
test tests::ignored_test... ok
test result: ok. 1 passed
0 failed
0 ignored
0 measured
0 filtered out
Doc-tests tmp-ignore
running 0 tests
test result: ok. 0 passed
0 failed
0 ignored
0 measured
0 filtered out
Zusammenfassung
Herzlichen Glückwunsch! Sie haben das Lab zu Unit-Tests abgeschlossen. Sie können in LabEx weitere Labs absolvieren, um Ihre Fähigkeiten zu verbessern.