前回に続いて写経しながら要点だけこの記事に書き起こす形で実践Rustプログラミング入門のPart1のCharpter3-5~を読んでみる。
テスト
Rustでは機能のためのコードとそれをテストするコードを同一のファイルの中で書くことができる。
pub fn add(x:i32, y:i32) -> i32 { return x + y; } #[test] fn test_add() { assert_eq!(0, add(0, 0)) assert_eq!(1, add(0, 1)) assert_eq!(1, add(1, 0)) assert_eq!(2, add(1, 1)) }
このテスト関数を実行するにはcargo testコマンドを実行する。
assertマクロ
テストコード中の判定に使えるassertマクロには次のようなものがある。
- assert!()
- assert_eq!()
- assert_ne!()
#[test] fn assert_sample() { assert!(true); assert!(false, "panic! value={}", false); assert_eq!(true, true); assert_ne!(true, false); assert_eq!(true, false, "panic! value=({} {})", true, false); }
パニックを発生させるテスト
テストの中には、あえてパニックを発生させるテストを実施したいときがある。 そのときには、should_panicアトリビュートを使用する。
#[test] #[should_panic] fn test_panic() { panic!("expected panic"); }
普通ならpanic!マクロが実行されるのでテストは失敗するが今回のようにshould_panicアトリビュートの付いたテスト関数の場合は成功となる。逆にパニックを起こさなかった場合失敗となる。
普段は無視するテスト
#[ignore]アトリビュートを付けておくことで通常のcargo testでは実行されず無視されるようになる。
#[test] #[ignore] fn test_add_ignored() { assert_eq!(-2, add(-1, -1)) }
この無視されたテストコードは、cargo test --ignoredで実行できる。
テストモジュール
cargo new --libで新しいプロジェクトフォルダを作成すると、次のようなコードが自動で生成される。
#[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } }
#[cfg(test)]アトリビュートを指定したtestsモジュールは、cargo testを実行した時だけ、そのコードをコンパイルし、実行ファイルにそのコードを含めるようになる。
testモジュールを使用する利点は、テストコードでしか使わないヘルパー関数をこのモジュールに含めることができるので、通常のcargo buildしたバイナリファイルに不要なコードを入れないようにすることができる点。
同様に、testsモジュールの中で外部のモジュールをuseすると、テストの時だけインポートされるので、本番のバイナリファイルに不要なコードを入れずに済む。
fn add(a: i32, b: i32) -> i32 { a + b } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { assert_eq!(add(2, 2), 4); } }
use::super*は、親ディレクトリに含まれる構造体や関数をすべて読み込むイディオム。
testsディレクトリ
複数のモジュールにまたがった結合テストを実行する場合には、testsディレクトリを使う。
これまではsecディレクトリの中で機能を実装してきたが、testsディレクトリはそれとはまったく別のクレートという位置付け。
testsディレクトリはsrcディレクトリで実装したクレートを外部クレートとして使用し、そのテストを実施できる。 そのため公開されている関数でのみテストで使用できる。
例えばsrc/libsに次のようなコードがあったとする。
fn add(x: i32, y: i32) -> i32 { return x + y; }
これをテストするためのコードとしてtests/libs.rsに次のようなコードを書くことができる。
use test_code::add; #[test] fn integration_test() { assert_eq!(3, add(1, 2)); }