跳轉至

建立 2026-09-19 更新 2026-09-19

測試

Rust 內建測試框架,不需要額外安裝套件。用 #[test] 標記函式,再執行 cargo test 就會自動找出並執行所有測試。編譯器已經幫你擋掉許多錯誤,測試則負責驗證「邏輯是否正確」。

單元測試

慣例是把測試寫在同一個檔案的最下方,放進 tests 模組,並用 #[cfg(test)] 標記:這個模組只有在 cargo test 時才會被編譯,不會進入正式的執行檔。

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

pub fn divide(a: i32, b: i32) -> Option<i32> {
    if b == 0 { None } else { Some(a / b) }
}

#[cfg(test)]
mod tests {
    use super::*; // 引入外層模組的所有項目

    #[test]
    fn add_works() {
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    fn divide_by_zero_returns_none() {
        assert_eq!(divide(10, 0), None);
    }

    #[test]
    fn divide_normally() {
        assert_eq!(divide(10, 2), Some(5));
    }
}

執行:

cargo test

輸出會列出每個測試通過(ok)或失敗(FAILED)。

常用的斷言巨集

巨集 用途
assert!(條件) 條件必須為真
assert_eq!(左, 右) 兩邊必須相等,失敗時會印出兩邊的值
assert_ne!(左, 右) 兩邊必須不相等

這些巨集都可以附上自訂訊息:assert!(x > 0, "x 應該是正數,實際是 {x}")

預期會 panic 的測試

#[should_panic] 驗證「在錯誤情況下確實會失敗」:

pub fn get_item(items: &[i32], index: usize) -> i32 {
    if index >= items.len() {
        panic!("索引 {index} 超出範圍");
    }
    items[index]
}

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

    #[test]
    #[should_panic(expected = "超出範圍")]
    fn out_of_range_panics() {
        get_item(&[1, 2, 3], 10);
    }
}

回傳 Result 的測試

測試函式也可以回傳 Result,這樣就能在裡面使用 ?

#[cfg(test)]
mod tests {
    #[test]
    fn parse_works() -> Result<(), std::num::ParseIntError> {
        let n: i32 = "42".parse()?;
        assert_eq!(n, 42);
        Ok(())
    }
}

整合測試與文件測試

  • 整合測試:放在專案根目錄的 tests/ 資料夾(與 src/ 同層),每個檔案是獨立的 crate,只能使用你公開(pub)的介面,用來驗證各部分能否一起運作。要使用整合測試,專案必須是函式庫 crate(有 src/lib.rs)。
  • 文件測試:寫在文件註解 /// 中的程式碼區塊也會被 cargo test 執行,確保文件裡的範例不會過期。
/// 把兩個數字相加。
///
/// ```
/// let sum = my_crate::add(2, 3);
/// assert_eq!(sum, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

控制執行哪些測試

cargo test add            # 只執行名稱含 "add" 的測試
cargo test -- --nocapture # 顯示測試中 println! 的輸出
cargo test -- --ignored   # 執行被 #[ignore] 標記的耗時測試

測試預設會以多執行緒並行執行,因此測試之間不應依賴共同的外部狀態(例如同一個檔案)。

實用心法

  • 每修一個 bug,就先寫一個能重現它的測試,避免日後復發。
  • 測試名稱要能說明「預期的行為」,例如 divide_by_zero_returns_none
  • 測試放得離程式碼近,寫程式時養成順手補一個測試的習慣。

推薦影音

Rust 的測試

簡述:Let's Get Rusty 依照官方書籍第 11 章製作,介紹 #[test]、斷言巨集與 cargo test 的用法。

下一頁:智慧指標