鍍金池/ 教程/ Java/ 測試
While 循環(huán)
宏命令
模式
Rust 嵌入到其他語言
變量綁定
if
發(fā)布通道
Lang 項(xiàng)目
匹配
文檔
棧和堆
不依賴 stdlib
原始指針
條件編譯
type 別名
關(guān)聯(lián)類型
全類型
詞匯表
基本類型
Hello, world!
測試
箱和模塊
字符串
向量
引用與借用
所有權(quán)
內(nèi)斂函數(shù)
基準(zhǔn)測試
Nightly Rust
for 循環(huán)
特征
特征的對象
鏈接參數(shù)
介紹
'Deref'強(qiáng)制轉(zhuǎn)換
枚舉
內(nèi)聯(lián)匯編
泛型
方法語法
函數(shù)
外部函數(shù)接口
盒語法和模式
安裝 Rust
unsafe    
生存期
切片模式
Borrow 和 AsRef
If let
學(xué)習(xí) Rust
“常量”和“靜態(tài)”
語法和語義
迭代器
相關(guān)學(xué)術(shù)研究
通用函數(shù)調(diào)用語法
哲學(xué)家就餐問題
類型轉(zhuǎn)換
閉包
并發(fā)性
Hello, Cargo!
屬性
注釋
結(jié)構(gòu)體
編譯器插件
高效 Rust
相關(guān)常量
猜謎游戲
可變性
錯(cuò)誤處理
新手入門
操作符和重載

測試

測試

程序測試是一個(gè)非常有效的方法,它可以有效的暴漏程序中的缺陷,但對于暴漏缺陷來說,這還是遠(yuǎn)遠(yuǎn)不夠的。
—— Edsger W. Dijkstra,"卑微的程序員" (1972)

讓我們來談?wù)勅绾螠y試 Rust 代碼。我們將談?wù)摬皇鞘裁礈y試 Rust 代碼正確的方法。關(guān)于正確和錯(cuò)誤地編寫測試的方式有很多的流派。所有這些方法都使用相同的基本工具,因此,我們將向您展示使用它們的語法。

測試屬性

Rust 中一個(gè)最簡單的測試是一個(gè)函數(shù),它使用 test 屬性注釋。讓我們使用 Cargo 做一個(gè)叫加法器的新項(xiàng)目:

    $ cargo new adder
    $ cd adder

當(dāng)你做一個(gè)新項(xiàng)目時(shí),Cargo 將自動(dòng)生成一個(gè)簡單的測試。下面即是 src/lib.rs 的內(nèi)容:

    #[test]
    fn it_works() {
    }

注意 #[test]。該屬性表明,這是一個(gè)測試函數(shù),目前還沒有函數(shù)體。我們可以使用 Cargo test 運(yùn)行這個(gè)測試:

    $ cargo test
       Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

    running 1 test
    test it_works ... ok

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

       Doc-tests adder

    running 0 tests

    test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

Cargo 編譯和運(yùn)行我們的測試。這里有兩組輸出:一個(gè)用于我們寫的測試,另一個(gè)用于文檔測試。稍后我們將討論這一問題。現(xiàn)在,讓我們來看看這一行:

    test it_works ... ok

注意 it_works。這是來自我們的函數(shù)的名稱:

    fn it_works() {

我們還得到一個(gè)總結(jié):

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

那么為什么我們的測試能夠通過呢?任何非 panic 的測試都可以通過,任何 panic 的測試都會(huì)失敗。讓我們來看一個(gè)失敗的測試:

    #[test]
    fn it_works() {
        assert!(false);
    }

assert! 一種 Rust 提供的宏,它需要一個(gè)參數(shù):如果參數(shù)是true,什么也不會(huì)發(fā)生。如果參數(shù)是 false,它就成為 panic! 的。讓我們再次運(yùn)行我們的測試:

    $ cargo test
       Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

    running 1 test
    test it_works ... FAILED

    failures:

    ---- it_works stdout ----
    thread 'it_works' panicked at 'assertion failed: false', /home/steve/tmp/adder/src/lib.rs:3

    failures:
    it_works

    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured

    thread '<main>' panicked at 'Some tests failed', /home/steve/src/rust/src/libtest/lib.rs:247

Rust 表明我們的測試失?。?/p>

    test it_works ... FAILED

反映在結(jié)論中就是:

    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured

還可以得到一個(gè)非零的狀態(tài)代碼:

    $ echo $?
    101

如果你想將 cargo test 集成到其他工具,這是非常有用的。

我們可以用另一個(gè)屬性:should_panic 轉(zhuǎn)化我們的測試的失?。?/p>

#[test]
#[should_panic]
fn it_works() {
    assert!(false);
}

如果我們 panic!,這個(gè)測試會(huì)成功,如果我們完成,則測試會(huì)失敗。讓我們來試一試:

    $ cargo test
       Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

    running 1 test
    test it_works ... ok

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

       Doc-tests adder

    running 0 tests

    test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

Rust 提供另一個(gè)宏 assert_eq!,用來比較兩個(gè)參數(shù)是否相等:

    #[test]
    #[should_panic]
    fn it_works() {
        assert_eq!("Hello", "world");
    }

這個(gè)測試是否可以通過?因?yàn)榇嬖?should_panic 屬性,它可以通過:

    $ cargo test
       Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

    running 1 test
    test it_works ... ok

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

       Doc-tests adder

    running 0 tests

    test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

should_panic 測試很脆弱。很難保證測試不會(huì)因?yàn)橐粋€(gè)意想不到的原因而失敗。為了解決這個(gè)問題,可以在 should_panic 屬性中添加一個(gè)可選的參數(shù):expected。測試工具將確保錯(cuò)誤消息包含提供的文本。上面示例的安全版本是:

    #[test]
    #[should_panic(expected = "assertion failed")]
    fn it_works() {
        assert_eq!("Hello", "world");
    }

這就是所有的基礎(chǔ)讓我們來編寫一個(gè)“真正”的測試:

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

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

這是 assert_eq! 的一個(gè)非常常見的用法:使用一些已知的參數(shù)調(diào)用某些函數(shù)并與預(yù)期的輸出比較。

測試模塊

有一種方式,以這種方式我們現(xiàn)有的例子都是不符合慣例的:它缺少測試模塊。我們的示例的慣用寫作方式,如下所示:

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

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

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

這里有一些變化。第一個(gè)是引入帶有 cfg 屬性的 mod tests。模塊允許我們對所有的測試進(jìn)行分組,如果需要也可以定義 helper 函數(shù),這個(gè)函數(shù)不會(huì)成為我們 crate 的一部分。如果目前我們試圖運(yùn)行這些代碼,cfg 屬性只會(huì)編譯我們的測試代碼。這可以節(jié)省編譯時(shí)間,也保證了我們構(gòu)建的測試是完全正常的。

第二個(gè)變化是 use 聲明。因?yàn)槲覀冊谝粋€(gè)內(nèi)部模塊中,我們需要將我們的測試函數(shù)設(shè)置范圍。如果你有一個(gè)大的模塊,這可能就會(huì)很惱人,所以這是 glob 屬性的一種常見的使用方式。讓我們改變我們的 src/lib.rs 以便能夠使用它:

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

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

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

注意 use 行的不同使用方式?,F(xiàn)在,我們來運(yùn)行我們的測試:

    $ cargo test
    Updating registry `https://github.com/rust-lang/crates.io-index`
       Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

    running 1 test
    test tests::it_works ... ok

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

       Doc-tests adder

    running 0 tests

    test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

看,運(yùn)轉(zhuǎn)起來了!

當(dāng)前的慣例是使用測試模塊 “unit-style” 測試。任何只測試一個(gè)小功能都是有意義的。如果用 “integration-style” 測試替代會(huì)怎么樣呢?為此,我們引出了測試目錄。

測試目錄

為了編寫集成測試,讓我們做一個(gè)測試目錄,并把一個(gè) tests/lib.rs 文件放在里面,這是它的內(nèi)容:

    extern crate adder;

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

這類似于我們之前的測試,但略有不同。在代碼頂部有一個(gè) extern crate adder。這是因?yàn)樵跍y試目錄里測試是一個(gè)完全獨(dú)立的箱,所以我們需要導(dǎo)入我們的函數(shù)庫。這也是為什么 tests 是一個(gè)編寫集成風(fēng)格測試的合適的地方:他們使用函數(shù)庫和其他消費(fèi)者。

讓我們運(yùn)行它們:

    $ cargo test
       Compiling adder v0.0.1 (file:///home/you/projects/adder)
     Running target/adder-91b3e234d4ed382a

    running 1 test
    test tests::it_works ... ok

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

     Running target/lib-c18e7d3494509e74

    running 1 test
    test it_works ... ok

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

       Doc-tests adder

    running 0 tests

    test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

現(xiàn)在我們有三個(gè)部分:我們之前的測試在運(yùn)行,現(xiàn)在這個(gè)新的也在運(yùn)行。

這就是所有的 tests 目錄。這里不需要測試模塊,因?yàn)檎露际菍W⒂跍y試的。

讓我們最后檢查一下第三部分:文檔測試。

文檔測試

沒有什么是比帶有示例的文檔更好的了。沒有什么是比不能真正工作的例子更糟的了,一直以來文檔編寫已經(jīng)改變了代碼習(xí)慣。為此,Rust 支持自動(dòng)運(yùn)行你的文檔中的示例。這里有一個(gè)完整的 src/lib.rs 的例子:


//! The `adder` crate provides functions that add numbers to other numbers.
//!
//! # Examples
//!
//! ```
//! assert_eq!(4, adder::add_two(2));
//! ```

/// This function adds two to its argument.
///
/// # Examples
///
/// ```
/// use adder::add_two;
///
/// assert_eq!(4, add_two(2));
/// ```
pub fn add_two(a: i32) -> i32 {
    a + 2
}

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

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

注意:模塊級(jí)文檔使用 //!,函數(shù)文檔使用 ///。Rust 的文檔支持 Markdown 中評(píng)論,所以三重斜線標(biāo)志代碼塊。包含 # Examples 部分是一種慣例,以下所示。

讓我們再次運(yùn)行測試:

    $ cargo test
       Compiling adder v0.0.1 (file:///home/steve/tmp/adder)
     Running target/adder-91b3e234d4ed382a

    running 1 test
    test tests::it_works ... ok

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

     Running target/lib-c18e7d3494509e74

    running 1 test
    test it_works ... ok

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

       Doc-tests adder

    running 2 tests
    test add_two_0 ... ok
    test _0 ... ok

    test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured

現(xiàn)在我們運(yùn)行了所有三種測試!注意這些測試文檔的名稱:the_0 生成模塊測試,add_two_0 生成功能測試。當(dāng)你添加更多的例子,這些名字會(huì)自動(dòng)增量(例如 add_two_1)。