iT邦幫忙

2023 iThome 鐵人賽

DAY 19
1
Software Development

為你自己學 Ru.....st系列 第 19

[為你自己學 Rust] 測試(Test)

  • 分享至 

  • xImage
  •  

不管在哪個程式語言,雖然有些人可能不會或不習慣寫測試,但就算不寫也都知道測試都是很重要的部分,撰寫測試有助於確保程式碼代碼的正確性。前面我們也見識過 Rust 編譯器本身的龜毛,可以在編譯階段就盡量抓出可能的問題,如果能再加上測試應該可以再減低程式發生錯誤的機率。

即使不使用外部套件,Rust 本身也有支援測試的寫法,就是我們在上個章節介紹到的「屬性(Attributes)」。測試本身不算難寫,但對沒常寫測試的人來說比較難的是不知道從哪裡下手或是該怎麼測試。我就先用個簡單的 BMI 計算機來試一下手感。

為求簡單展示,我就先把測試寫在 main.rs 裡:

#[cfg(test)]
mod bmi {
    // 內容還沒寫
}

fn main() {}

這裡的 #[cfg(test)] 是我們在上個章節介紹到的屬性。雖然我這個是直接測試寫在 main.rs 裡,但 #[cfg(test)] 這個屬性會告訴 Rust 編譯器以下這段程式碼塊只有在執行測試時才會被編譯進去。

接著那個 mod 的寫法我們應該在下個章節就會介紹到,它是模組的意思,至於這模組要叫什麼名字並沒有強制規定,因為我想要寫一個 BMI 的計算機,我這裡就先給它一個 bmi。接著執行 cargo test 指令,就可以開始跑測試了:

$ cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.08s
     Running unittests src/main.rs (target/debug/deps/hello_rust-39c79d4f99e78c83)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

因為我們什麼都還沒寫,所以結果就是 0 個測試。

補上測試

先來寫個練手的測試:

#[cfg(test)]
mod bmi {
    #[test]
    fn dummy() {
        let result = 1 + 2;
        assert_eq!(result, 3);
    }
}

我先放一個看起來很呆的測試,就只是展示 1 + 2 等於 3,這應該沒什麼問題,中間那個 #[test] 標記是告訴 Rust 編譯器這是一個測試的函數,它要在 cargo test 跑測試的時候被呼叫,執行之後的結果變成:

$ cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.11s
     Running unittests src/main.rs (target/debug/deps/hello_rust-39c79d4f99e78c83)

running 1 test
test bmi::dummy ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

從底下的統計就看的出來跑了幾個測試,幾個成功,幾個失敗。你可以試著把 #[test] 拿掉看看,這個測試就不會在 cargo test 的時候被執行了。

是時候把該寫的測試補上去了:

#[test]
fn test_calc() {
    let result = bmi_calc(180, 65);
    assert_eq!(result, 20.1);
}

因為 bmi_calc() 還沒寫,這時候執行測試一定會壞:

$ cargo test
error[E0425]: cannot find function `bmi_calc` in this scope
11 |         let result = bmi_calc(180, 65);
   |                      ^^^^^^^^ not found in this scope

Rust 編譯器說找不到 bmi_calc(),廢話,我們就還沒寫,如果執行沒有出錯,不是你有養幫你寫 code 的小精靈,就是你還沒睡醒。既然還沒寫,那我們現在就把它寫完吧:

#[cfg(test)]
mod bmi {
    use crate::bmi_calc;

    #[test]
    fn dummy() {
        let result = 1 + 2;
        assert_eq!(result, 3);
    }

    #[test]
    fn test_calc() {
        let result = bmi_calc(180, 65);
        assert_eq!(result, 20.1);
    }
}

fn bmi_calc<T, U>(height: T, weight: U) -> f64
where
    T: Into<f64>,
    U: Into<f64>,
{
    let h = height.into() / 100.0;
    let bmi = weight.into() / (h * h);

    (bmi * 10.0).round() / 10.0
}

fn main() {}

這裡的 use crate::bmi_calc; 是表示要使用 crate 模組裡的 bmi_calc 函數,我們會在下個章節來介紹 Rust 的模組功能。

bmi_calc() 的實作就應該沒什麼驚喜了,就是用我們前面學到的泛型,讓它的參數可以傳整數也可以傳浮點數 :)


上一篇
[為你自己學 Rust] 屬性(Attributes)
下一篇
[為你自己學 Rust] 模組(Module)
系列文
為你自己學 Ru.....st30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
ak8893893
iT邦新手 5 級 ‧ 2024-03-03 09:57:13

補充: assert_eq!(result, 20.1)

在 Rust 中,assert_eq!(result, 20.1); 是一個用來測試的宏,它用來確保兩個值相等。 這個特定的語句是在測試bmi_calc函數的輸出結果。

assert_eq!宏接受兩個參數,並比較它們是否相等。
如果兩個參數相等,這個測試就會通過,沒有任何訊息輸出。
如果不相等,測試將會失敗,並且會報告一個錯誤,顯示預期值(expected value)和實際得到的值(actual value)。

我要留言

立即登入留言