ch11-02-running-tests.md
commit 1721a106f78c037ca3074d9c2d5a8cf9c9852cf7
就像 cargo run
會(huì)編譯代碼并運(yùn)行生成的二進(jìn)制文件一樣,cargo test
在測(cè)試模式下編譯代碼并運(yùn)行生成的測(cè)試二進(jìn)制文件??梢灾付钚袇?shù)來(lái)改變 cargo test
的默認(rèn)行為。例如,cargo test
生成的二進(jìn)制文件的默認(rèn)行為是并行的運(yùn)行所有測(cè)試,并截獲測(cè)試運(yùn)行過(guò)程中產(chǎn)生的輸出,阻止他們被顯示出來(lái),使得閱讀測(cè)試結(jié)果相關(guān)的內(nèi)容變得更容易??梢詫⒁徊糠置钚袇?shù)傳遞給 cargo test
,而將另外一部分傳遞給生成的測(cè)試二進(jìn)制文件。為了分隔這兩種參數(shù),需要先列出傳遞給 cargo test
的參數(shù),接著是分隔符 --
,再之后是傳遞給測(cè)試二進(jìn)制文件的參數(shù)。運(yùn)行 cargo test --help
會(huì)提示 cargo test
的有關(guān)參數(shù),而運(yùn)行 cargo test -- --help
可以提示在分隔符 --
之后使用的有關(guān)參數(shù)。
當(dāng)運(yùn)行多個(gè)測(cè)試時(shí), Rust 默認(rèn)使用線程來(lái)并行運(yùn)行。這意味著測(cè)試會(huì)更快地運(yùn)行完畢,所以你可以更快的得到代碼能否工作的反饋。因?yàn)闇y(cè)試是在同時(shí)運(yùn)行的,你應(yīng)該確保測(cè)試不能相互依賴,或依賴任何共享的狀態(tài),包括依賴共享的環(huán)境,比如當(dāng)前工作目錄或者環(huán)境變量。
舉個(gè)例子,每一個(gè)測(cè)試都運(yùn)行一些代碼,假設(shè)這些代碼都在硬盤上創(chuàng)建一個(gè) test-output.txt 文件并寫入一些數(shù)據(jù)。接著每一個(gè)測(cè)試都讀取文件中的數(shù)據(jù)并斷言這個(gè)文件包含特定的值,而這個(gè)值在每個(gè)測(cè)試中都是不同的。因?yàn)樗袦y(cè)試都是同時(shí)運(yùn)行的,一個(gè)測(cè)試可能會(huì)在另一個(gè)測(cè)試讀寫文件過(guò)程中修改了文件。那么第二個(gè)測(cè)試就會(huì)失敗,并不是因?yàn)榇a不正確,而是因?yàn)闇y(cè)試并行運(yùn)行時(shí)相互干擾。一個(gè)解決方案是使每一個(gè)測(cè)試讀寫不同的文件;另一個(gè)解決方案是一次運(yùn)行一個(gè)測(cè)試。
如果你不希望測(cè)試并行運(yùn)行,或者想要更加精確的控制線程的數(shù)量,可以傳遞 --test-threads
參數(shù)和希望使用線程的數(shù)量給測(cè)試二進(jìn)制文件。例如:
$ cargo test -- --test-threads=1
這里將測(cè)試線程設(shè)置為 1
,告訴程序不要使用任何并行機(jī)制。這也會(huì)比并行運(yùn)行花費(fèi)更多時(shí)間,不過(guò)在有共享的狀態(tài)時(shí),測(cè)試就不會(huì)潛在的相互干擾了。
默認(rèn)情況下,當(dāng)測(cè)試通過(guò)時(shí),Rust 的測(cè)試庫(kù)會(huì)截獲打印到標(biāo)準(zhǔn)輸出的所有內(nèi)容。比如在測(cè)試中調(diào)用了 println!
而測(cè)試通過(guò)了,我們將不會(huì)在終端看到 println!
的輸出:只會(huì)看到說(shuō)明測(cè)試通過(guò)的提示行。如果測(cè)試失敗了,則會(huì)看到所有標(biāo)準(zhǔn)輸出和其他錯(cuò)誤信息。
例如,示例 11-10 有一個(gè)無(wú)意義的函數(shù),它打印出其參數(shù)的值并接著返回 10。接著還有一個(gè)會(huì)通過(guò)的測(cè)試和一個(gè)會(huì)失敗的測(cè)試:
文件名: 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);
}
}
示例 11-10:一個(gè)調(diào)用了 println!
的函數(shù)的測(cè)試
運(yùn)行 cargo test
將會(huì)看到這些測(cè)試的輸出:
$ cargo test
Compiling silly-function v0.1.0 (file:///projects/silly-function)
Finished test [unoptimized + debuginfo] target(s) in 0.58s
Running unittests (target/debug/deps/silly_function-160869f38cff9166)
running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok
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
error: test failed, to rerun pass '--lib'
注意輸出中不會(huì)出現(xiàn)測(cè)試通過(guò)時(shí)打印的內(nèi)容,即 I got the value 4
。因?yàn)楫?dāng)測(cè)試通過(guò)時(shí),這些輸出會(huì)被截獲。失敗測(cè)試的輸出 I got the value 8
,則出現(xiàn)在輸出的測(cè)試摘要部分,同時(shí)也顯示了測(cè)試失敗的原因。
如果你希望也能看到通過(guò)的測(cè)試中打印的值,也可以在結(jié)尾加上 --show-output
告訴 Rust 顯示成功測(cè)試的輸出。
$ cargo test -- --show-output
使用 --show-output
參數(shù)再次運(yùn)行示例 11-10 中的測(cè)試會(huì)顯示如下輸出:
$ cargo test -- --show-output
Compiling silly-function v0.1.0 (file:///projects/silly-function)
Finished test [unoptimized + debuginfo] target(s) in 0.60s
Running unittests (target/debug/deps/silly_function-160869f38cff9166)
running 2 tests
test tests::this_test_will_fail ... FAILED
test tests::this_test_will_pass ... ok
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
error: test failed, to rerun pass '--lib'
有時(shí)運(yùn)行整個(gè)測(cè)試集會(huì)耗費(fèi)很長(zhǎng)時(shí)間。如果你負(fù)責(zé)特定位置的代碼,你可能會(huì)希望只運(yùn)行與這些代碼相關(guān)的測(cè)試。你可以向 cargo test
傳遞所希望運(yùn)行的測(cè)試名稱的參數(shù)來(lái)選擇運(yùn)行哪些測(cè)試。
為了展示如何運(yùn)行部分測(cè)試,示例 11-11 為 add_two
函數(shù)創(chuàng)建了三個(gè)測(cè)試,我們可以選擇具體運(yùn)行哪一個(gè):
文件名: 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));
}
}
示例 11-11:不同名稱的三個(gè)測(cè)試
如果沒(méi)有傳遞任何參數(shù)就運(yùn)行測(cè)試,如你所見(jiàn),所有測(cè)試都會(huì)并行運(yùn)行:
$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.62s
Running unittests (target/debug/deps/adder-92948b65e88960b4)
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
Doc-tests adder
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
可以向 cargo test
傳遞任意測(cè)試的名稱來(lái)只運(yùn)行這個(gè)測(cè)試:
$ cargo test one_hundred
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.69s
Running unittests (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
只有名稱為 one_hundred
的測(cè)試被運(yùn)行了;因?yàn)槠溆鄡蓚€(gè)測(cè)試并不匹配這個(gè)名稱。測(cè)試輸出在摘要行的結(jié)尾顯示了 2 filtered out
表明還存在比本次所運(yùn)行的測(cè)試更多的測(cè)試被過(guò)濾掉了。
不能像這樣指定多個(gè)測(cè)試名稱;只有傳遞給 cargo test
的第一個(gè)值才會(huì)被使用。不過(guò)有運(yùn)行多個(gè)測(cè)試的方法。
我們可以指定部分測(cè)試的名稱,任何名稱匹配這個(gè)名稱的測(cè)試會(huì)被運(yùn)行。例如,因?yàn)轭^兩個(gè)測(cè)試的名稱包含 add
,可以通過(guò) cargo test add
來(lái)運(yùn)行這兩個(gè)測(cè)試:
$ cargo test add
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.61s
Running unittests (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
這運(yùn)行了所有名字中帶有 add
的測(cè)試,也過(guò)濾掉了名為 one_hundred
的測(cè)試。同時(shí)注意測(cè)試所在的模塊也是測(cè)試名稱的一部分,所以可以通過(guò)模塊名來(lái)運(yùn)行一個(gè)模塊中的所有測(cè)試。
有時(shí)一些特定的測(cè)試執(zhí)行起來(lái)是非常耗費(fèi)時(shí)間的,所以在大多數(shù)運(yùn)行 cargo test
的時(shí)候希望能排除他們。雖然可以通過(guò)參數(shù)列舉出所有希望運(yùn)行的測(cè)試來(lái)做到,也可以使用 ignore
屬性來(lái)標(biāo)記耗時(shí)的測(cè)試并排除他們,如下所示:
文件名: src/lib.rs
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
#[ignore]
fn expensive_test() {
// 需要運(yùn)行一個(gè)小時(shí)的代碼
}
對(duì)于想要排除的測(cè)試,我們?cè)?nbsp;#[test]
之后增加了 #[ignore]
行?,F(xiàn)在如果運(yùn)行測(cè)試,就會(huì)發(fā)現(xiàn) it_works
運(yùn)行了,而 expensive_test
沒(méi)有運(yùn)行:
$ cargo test
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.60s
Running unittests (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
Doc-tests adder
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
expensive_test
被列為 ignored
,如果我們只希望運(yùn)行被忽略的測(cè)試,可以使用 cargo test -- --ignored
:
$ cargo test -- --ignored
Compiling adder v0.1.0 (file:///projects/adder)
Finished test [unoptimized + debuginfo] target(s) in 0.61s
Running unittests (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
Doc-tests adder
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
通過(guò)控制運(yùn)行哪些測(cè)試,你可以確保能夠快速地運(yùn)行 cargo test
。當(dāng)你需要運(yùn)行 ignored
的測(cè)試時(shí),可以執(zhí)行 cargo test -- --ignored
。如果你希望不管是否忽略都要運(yùn)行全部測(cè)試,可以運(yùn)行 cargo test -- --include-ignored
。
更多建議: