Rust 控制測(cè)試如何運(yùn)行

2023-03-22 15:10 更新
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ù)。

并行或連續(xù)的運(yùn)行測(cè)試

當(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ì)潛在的相互干擾了。

顯示函數(shù)輸出

默認(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'

通過(guò)指定名字來(lái)運(yùn)行部分測(cè)試

有時(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

運(yùn)行單個(gè)測(cè)試

可以向 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è)試的方法。

過(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è)試。

忽略某些測(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。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)