Closed10
The Rust Programming Language - Chapter 11 メモ
自動テストを書く
テストの記述法
- 必要なデータや状態をセットアップする。
- テスト対象のコードを走らせる。
- 結果が想定通りであることを断定(以下、アサーションという)する。
テスト関数の構成
- 最も単純には、Rustにおけるテストは
test
属性で注釈された関数- 属性:Rustコードの部品に関するメタデータ
- 例)
derive
属性
- 例)
- 関数に
#[test]
を付与すればテスト関数になる -
cargo test
でテストを実行できる
- 属性:Rustコードの部品に関するメタデータ
Rust
#[cfg(test)]
mod tests {
#[test] // これがあるとテスト関数になる
fn it_works() {
assert_eq!(2 + 2, 4); // assert_eq!マクロ
}
#[test]
fn another() {
//このテストを失敗させる
panic!("Make this test fail");
}
}
assert!マクロで結果を確認する
Rust
#[cfg(test)]
mod tests {
// 外部モジュール内のテスト配下にあるコードを内部モジュールのスコープに持っていく必要がある
// 外部モジュール内のテスト配下にあるコード = Rectangle
use super::*;
#[test]
fn larger_can_hold_smaller() {
let larger = Rectangle {
width: 8,
height: 7,
};
let smaller = Rectangle {
width: 5,
height: 1,
};
assert!(larger.can_hold(&smaller));
}
#[test]
fn smaller_cannot_hold_larger() {
let larger = Rectangle {
width: 8,
height: 7,
};
let smaller = Rectangle {
width: 5,
height: 1,
};
assert!(!smaller.can_hold(&larger));
}
}
assert_eq!とassert_ne!マクロで等値性をテストする
Rust
pub fn add_two(a: i32) -> i32 {
a + 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds_two() {
assert_eq!(4, add_two(2));
assert_ne!(3, add_two(2));
}
}
カスタムの失敗メッセージを追加する
Rust
pub fn greeting(name: &str) -> String {
format!("Hello {}!", name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn greeting_contains_name() {
let result = greeting("Carol");
assert!(
result.contains("Carol"),
"Greeting did not contain name, value was `{}`", // 第二引数で失敗メッセージを指定する
result
);
}
}
should_panicでパニックを確認する
Rust
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
Guess { value }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
// #[should_panic] // should_panic属性を追加する
#[should_panic(expected = "Guess value must be less than or equal to 100")]
fn greater_than_100() {
Guess::new(200);
}
}
Result<T, E>をテストで使う
Rust
#[cfg(test)]
mod tests {
#[test]
// #[should_panic] は使えない
fn it_works() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
// テスト失敗させる場合は Err を返す
Err(String::from("two plus two does not equal four"))
}
}
}
テストの実行のされ方を制御する
-
cargo run
- コードをコンパイルし、出来上がったバイナリを実行する
-
cargo test
- コードをテストモードでコンパイルし、出来上がったテストバイナリを実行する
テストを並行または連続して実行する
-
デフォルトでは、複数のテストは平行に実行される。
- テストが相互や共有された環境を含む状態に依存しているとダメ
- 作業対象ディレクトリや環境変数など
- テストが相互や共有された環境を含む状態に依存しているとダメ
-
テストを1つずつ実行するには、スレッド数を制限する
- テスト実行時のスレッド数の制御には
--test-threads
フラグを使う
- テスト実行時のスレッド数の制御には
cargo test -- --test-threads=1
関数の出力を表示する
Rust
fn prints_and_returns_10(a: i32) -> i32 {
//{}という値を得た
println!("I got the value {}", a);
10
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn this_test_will_pass() {
let value = prints_and_returns_10(4);
assert_eq!(10, value);
}
#[test]
fn this_test_will_fail() {
let value = prints_and_returns_10(8);
assert_eq!(5, value);
}
}
デフォルトでは I got the value 4
などが出力されない。
出力したければ --nocapture
フラグを使う
cargo test -- --nocapture
名前でテストの一部を実行する
Rust
pub fn add_two(a: i32) -> i32 {
a + 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_two_and_two() {
assert_eq!(4, add_two(2));
}
#[test]
fn add_three_and_two() {
assert_eq!(5, add_two(3));
}
#[test]
fn one_hundred() {
assert_eq!(102, add_two(100));
}
}
どれか1つのテストだけを実行したければ cargo test
につづけてテスト関数を渡す
cargo test one_hundred
複数のテストを実行するようフィルターをかける
以下のようにコマンドをうつと add
からはじまる複数のテストを実行する
cargo test add
特に要望のない限りテストを無視する
#[ignore]
属性を付与する
#![allow(unused)]
fn main() {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
#[ignore]
fn expensive_test() {
// 実行に1時間かかるコード
// code that takes an hour to run
}
ignore
されているテストのみを実行したい場合 --ignored
フラグを付与する
cargo test -- --ignored
テストの体系化
単体テスト
- 単体テストは、テスト対象となるコードと共に、srcディレクトリの各ファイルに置く
- 慣習:各ファイルにtestsという名前のモジュールを作り、テスト関数を含ませ、 そのモジュールをcfg(test)で注釈する
-
#[cfg(test)]
の注釈は、cargo build
を走らせたときではなく、cargo test
を走らせたときにだけ、テストコードをコンパイルし実行するよう指示するもの。 - 単体テストは、コードと同じファイルにテストコードを置くため
#[cfg(test)]
で注釈する必要がある。 - Rustの公開性規則により、非公開関数もテストすることが可能
Rust
pub fn add_two(a: i32) -> i32 {
internal_adder(a, 2)
}
fn internal_adder(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn internal() {
// pub で公開はしていないがテスト可能
assert_eq!(4, internal_adder(2, 2));
}
}
結合テスト
-
tests
ディレクトリ-
src
と同じ階層に作成すると、Cargo はこのディレクトリに結合テストのファイルがあると認識する。 - Cargo はそれぞれのファイルを個別のクレートとしてコンパイルする。
-
Rust
// testsディレクトリのテストはそれぞれ個別のクレートであるため、 各々ライブラリをインポートする必要があるため
extern crate adder;
#[test]
fn it_adds_two() {
assert_eq!(4, adder::add_two(2));
}
このスクラップは2021/07/17にクローズされました