やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rustのテストコードを書く

 assert_eq!マクロや#[should_panic]注釈など。

成果物

参考

プロジェクト作成

$ cargo new adder --lib

テスト実行

$ cd adder
$ cargo test

雛形コード

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

アサーション

  • assert!(bool): 真なら成功
  • assert_eq!(T, T): 同一なら成功
  • assert_ne!(T, T): 同一でないなら成功

エラーメッセージ

assert!

---- モジュール名::関数名 stdout ----
thread 'モジュール名::関数名' panicked at 'assertion failed:
コード', ファイルパス:行:列

assert_eq!, assert_ne!

---- モジュール名::関数名 stdout ----
thread 'モジュール名::関数名' panicked at 'assertion failed: `(left == right)`
  left: `4`,
 right: `5`', ファイルパス:行:列

追加

fn add_two(a: i32) -> i32 { a + 3 }
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn is_add_two_msg() { assert_eq!(4, add_two(2), "テストに失敗しましたわ。: {}", 4); }
}
---- tests::is_add_two_msg stdout ----
thread 'tests::is_add_two_msg' panicked at 'assertion failed: `(left == right)`
  left: `4`,
 right: `5`: テストに失敗しましたわ。: 4', src/lib.rs:11:27

#[cfg(test)]

 cargo testコマンド実行時のみビルドする。

#[test]

 テスト関数である。(cargo test時にテストとして認識する)

#[should_panic]

pub struct Guess {
    value: u32,
}
impl Guess {
    pub fn new(value: u32) -> Guess {
        if value < 1 || value > 100 { panic!("0〜100までの整数でなければならない。: {}", value); }
        Guess { value }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    #[should_panic]
    fn greater_than_100() { Guess::new(200); }
}
$ cargo test
...
running 1 test
test tests::greater_than_100 ... ok

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

 パニックしたのに強制終了されず、テスト成功としてカウントされている。これで異常系の確認もできる。

 もし、panic!コメントアウトしたら?

//        if value < 1 || value > 100 { panic!("0〜100までの整数でなければならない。: {}", value); }

 テスト失敗。ログ出力をみてもよくわからない。テストコードに#[should_panic]があるので、パニックしなかったから失敗したと予想できる。つまり、コードを見なければわからない。

running 1 test
test tests::greater_than_100 ... FAILED

failures:

failures:
    tests::greater_than_100

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

#[should_panic(expected = ...)]

 パニック時のメッセージを指定できる。種別が異なるパニックとは切り分けてテストできる。

pub struct Guess {
    value: u32,
}
impl Guess {
    pub fn new(value: u32) -> Guess {
        if value < 1 || value > 100 { panic!("0〜100までの整数でなければならない。: {}", value); }
        Guess { value }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    #[should_panic(expected = "0〜100までの整数でなければならない。: 199")]
//    #[should_panic(expected = "0〜100までの整数でなければならない。: 200")]
    fn greater_than_100() { Guess::new(200); }
}
---- tests::greater_than_100 stdout ----
thread 'tests::greater_than_100' panicked at '0〜100までの整数でなければならない。: 200', src/lib.rs:6:39
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
note: Panic did not include expected string '0〜100までの整数でなければならない。: 199'

failures:
    tests::greater_than_100

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

まとめ

注釈 意味
#[cfg(test)] cargo test時のみビルドする
#[test] cargo testでテスト対象とする
#[should_panic(expected = "パニック時のメッセージ")] パニックすべきテストである
マクロ 意味
assert!(bool) 引数が真ならテスト成功
assert_eq!(T, T) 引数が同一ならテスト成功
assert_ne!(T, T) 引数が同一でないならテスト成功

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

前回まで