俗話說:「測試寫得好,獎金少不了。」
有經驗的開發人員通常會通過單元測試來保證**基本邏輯的正確性。如果你是一名新手開發者,並且還沒體會到單元測試的好處,那麼建議你先讀一下我之前的一篇文章**潔癖系列(七):單元測試的地位。
寫單元測試一般需要三個步驟:
準備測試用例,測試用例要能覆蓋盡可能多的**
執行需要測試的**
判斷結果,是否是你希望得到的結果
了解了這些以後,我們就來看看在rust中應該怎麼寫單元測試。
首先我們建立乙個library專案
$ cargo new adder --lib
created library `adder` project
然後在src/lib.rs檔案中開始寫測試**
#[cfg(test)]
mod tests
}
此時在命令列執行cargo test
就會得到測試結果
可以看到,結果顯示,rust執行了一項測試並且測試通過。後面的doc-tests我們先放下,以後再聊。
當然,這並不是我們常見的測試,在日常開發中,我們通常是先寫我們的業務**然後再對各個函式進行單元測試,最後還會對某個模組進行整合測試。那麼我們就來模擬一下日常開發過程中應該如何來寫測試。
我們仍然是用上面的專案,先來在src/lib.rs中寫一段「業務**」
pub fn add_two(a: i32) -> i32
fn internal_adder(a: i32, b: i32) -> i32
這是一段非常簡單的**,對外暴露的函式只是乙個加2的功能,內部呼叫了乙個兩數相加的函式。現在我們就對這個內部函式做乙個單元測試。
#[cfg(test)]
mod tests
}
在測試模組中,如果想要使用我們業務**中的函式,就需要通過use super::*;
將其引入可用範圍。接著,還是執行cargo test
,測試結果與剛才類似。
測了半天全是通過的沒什麼意思,單元測試真正的作用是要發現**中的問題,所以我們來嘗試乙個錯誤的試一下。假設我們希望2+2等於5。
rust就會將錯誤棧列印出來,根據結果提示,這並不是完整的錯誤棧,我們還可以將rust_backtrace設定為full來檢視更加詳細的資訊。這裡我就不做演示了。
接下來我們再演示一下整合測試。我們通常將整合測試單獨放到乙個目錄中,在lib.rs檔案中,rust識別測試mod的名稱是tests,同樣的,我們在src下建立tests目錄。tests目錄下就是我們的所有整合測試**。
如圖,integration_test是我們測試**的檔案,common目錄下的mod.rs檔案中是一些整合測試必要的配置。這裡我們只是放了乙個空的setup函式。
在整合測試中,我們就要像正常他人使用我們的**時那樣來進行測試,首先需要將我們的mod引入到可用範圍,當然還需要加上common的mod。
use adder;
mod common;
#[tests]
fn it_adds_two()
接著就可以測試我們對外暴露的函式了。
ok,整合測試的方法我們也掌握了。現在來看看一直被我們忽略的doc-tests吧。
我們已經知道,rust中的注釋是雙斜線//
,像我們剛剛寫的library**,如果想要把它發布到crate.io上讓別人使用,那麼我們就需要增加相應的文件,這裡文件的每行都應該是三斜線///
開頭,而文件中也應該放一些例子供他人參考。
/// adds two to the number given.
////// # examples
////// ```
/// let arg = 5;
/// let answer = adder::add_two(arg);
////// assert_eq!(7, answer);
/// ```
pub fn add_two(a: i32) -> i32
現在我給add_two函式加上了文件,我們再次執行cargo test
命令。
現在我們就明白了,doc-tests測試就是執行我們文件中的例子。
到目前為止,我們已經知道了在rust中如何寫測試**了。接下來我們再來了解幾個比較常用的特性。
執行指定的測試**
我們在開發過程中肯定不會每次都去跑全量的單元測試,那樣太浪費時間了。通常是我們開發完乙個功能之後,編寫對應的單元測試,然後單獨跑這個測試。那麼rust中能不能單獨跑乙個單元測試呢?答案是肯定的。
相信細心的同學已經發現了,rust測試結果中,是針對每個測試單獨統計結果,並且每個測試都有自己的名字,像我們前面寫的it_works
和internal
。假設我們的**中同時存在這兩個函式,如果你想要單獨跑internal這乙個測試,就可以使用cargo test internal
命令。
你也可以使用這種方法來執行多個名稱類似的測試,假如我們有名稱為internal_a
的測試,那麼執行cargo test internal
命令時它也會被執行。
忽略某個測試
當我們有乙個測試執行時間非常長的時候,我們一般不會輕易去執行,這時如果你想要執行多個測試,除了用我們上面提到的方法,去指定不同的名稱列表以外。還可以把這個測試忽略掉。
現在我不想執行internal
測試了,只需要對**進行如下改動:
#[test]
#[ignore]
fn internal()
這時再來執行測試,結果如圖所示。
我們發現此時internal
測試已經被忽略了。
測試異常情況
除了測試**邏輯正常的情況,我們有時還需要測試一些異常情況,比如接收到非法引數時程式能否返回我們希望看到的異常。
我們首先來看一下如何測試程式返回異常資訊。
rust為我們提供了乙個叫做should_panic的註解。我們可以使用它來測試程式是否返回異常:
pub fn add_two(a: i32) -> i32
fn internal_adder(a: i32, b: i32) -> i32
a + b
}#[cfg(test)]
mod tests
}
此時我們執行測試時就會發現internal測試通過,因為它發生了執行緒恐慌,這是我們希望看到的結果。
另外,我們還可以再指定我們具體期望的異常,那麼就可以在should_panic後面加上expected引數。
#[test]
#[should_panic(expected = "a should be positive")]
fn internal()
大家可以自行執行一下這段測試**看看效果。
文中我向大家介紹了在rust中如何進行單元測試、整合測試,還有比較特殊的文件測試。最後還介紹了3種常見的測試特性。
最後想友情提醒大家一下,在開發過程中,不要寫完一堆功能後再開始寫單元測試,這時你很有可能會因為測試**過於繁瑣而放棄。建議大家每寫乙個功能,隨即開始進行單元測試,這樣也能立即看到自己的**的執行效果,提高成就感。這就是所謂的「步步為營」。
步步為營 79 快取
快取cache,一種空間換取時間的技術,適用於經常訪問,不常修改的資料.1 寫入快取 1.1 方法一 cache message ab 1.2 方法二 cache.insert message ab 1.3 其他過載 insert string key,object value,cachedepen...
步步為營 50 事務
說明 比較常用 1 事務的四大特性 1.1 原子性atomicity 乙個事務中包含的多個sql語句,要麼同時成功,要麼同時失敗.1.2 一致性consistency 事務必須使資料庫從從乙個一致性狀態變成另外乙個一致性狀態.銀行轉賬 1.3 隔離性 isolation 各個事務執行互不干擾 鎖 1...
io nio socket步步為營(三)NIO
原理 運用reactor模式 selector是核心 分發器a multiplexor of selectablechannel objects。能檢測任意個註冊過的channel上的事件,並分發事件,內部實現不用考慮,封裝的好處。client沒必要用nio,使用中的client server,需要...