テストの記述法

テストは、非テストコードが想定された手段で機能していることを実証するRustの関数です。 テスト関数の本体は、典型的には以下の3つの動作を行います:

  1. 必要なデータや状態をセットアップする。
  2. テスト対象のコードを走らせる。
  3. 結果が想定通りかアサーションする。

Rustが、特にこれらの動作を行うテストを書くために用意している機能を見ていきましょう。 これには、test属性、いくつかのマクロ、should_panic属性が含まれます。

テスト関数の解剖

最も単純には、Rustにおけるテストはtest属性で注釈された関数のことです。属性とは、 Rustコードの欠片に関するメタデータです; 一例を挙げれば、構造体とともに第5章で使用したderive属性です。 関数をテスト関数に変えるには、fnの前に#[test]を付け加えるのです。 cargo testコマンドでテストを実行したら、コンパイラはtest属性で注釈された関数を走らせるテスト用バイナリをビルドし、 各テスト関数が通過したか失敗したかを報告します。

第7章で、Cargoで新規ライブラリプロジェクトを作成した時に、テスト関数が含まれるテストモジュールが自動で生成されたことを見かけました。 このモジュールのおかげでテストを書き始めることができるので、新しいプロジェクトを立ち上げる度に、 テスト関数の正確な構造と記法を調べる必要がなくなるわけです。必要なだけ追加のテスト関数とテストモジュールは追加することができます。

実際にテストすることなしにテンプレートのテストが生成されるのを実験することでテストの動作法の一部を探求しましょう。 それから、自分で書いた何らかのコードを呼び出し、振る舞いが正しいかアサーションする現実世界のテストを書きましょう。

adderという新しいライブラリプロジェクトを生成しましょう:

$ cargo new adder --lib
     Created library `adder` project
$ cd adder

adderライブラリのsrc/lib.rsファイルの中身は、リスト11-1のような見た目のはずです。

ファイル名: src/lib.rs

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

リスト11-1: cargo newで自動生成されたテストモジュールと関数

とりあえず、最初の2行は無視し、関数に集中してその動作法を見ましょう。 fn行の#[test]注釈に注目してください: この属性は、これがテスト関数であることを示唆しますので、 テスト実行機はこの関数をテストとして扱うとわかるのです。さらに、testsモジュール内には非テスト関数を入れ込み、 一般的なシナリオをセットアップしたり、共通の処理を行う手助けをしたりもできるので、 #[test]属性でどの関数がテストかを示唆する必要があるのです。

関数本体は、assert_eq!マクロを使用して、2 + 2が4に等しいことをアサーションしています。 このアサーションは、典型的なテストのフォーマット例をなしているわけです。走らせてこのテストが通ることを確かめましょう。

cargo testコマンドでプロジェクトにあるテストが全て実行されます。リスト11-2に示したようにですね。

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished dev [unoptimized + debuginfo] target(s) in 0.22 secs
     Running target/debug/deps/adder-ce99bcc2479f4607

running 1 test
test tests::it_works ... ok

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

   Doc-tests adder

running 0 tests

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

リスト11-2: 自動生成されたテストを走らせた出力

Cargoがテストをコンパイルし、走らせました。Compiling, Finished, Runningの行の後にrunning 1 testの行があります。 次行が、生成されたテスト関数のit_worksという名前とこのテストの実行結果、okを示しています。 テスト実行の総合的なまとめが次に出現します。test result:ok.というテキストは、 全テストが通ったことを意味し、1 passed; 0 failedと読める部分は、通過または失敗したテストの数を合計しているのです。

無視すると指定したテストは何もなかったため、まとめは0 ignoredと示しています。 また、実行するテストにフィルタをかけもしなかったので、まとめの最後に0 filtered outと表示されています。 テストを無視することとフィルタすることに関しては次の節、「テストの実行され方を制御する」で語ります。

0 measuredという統計は、パフォーマンスを測定するベンチマークテスト用です。 ベンチマークテストは、本書記述の時点では、ナイトリ版のRustでのみ利用可能です。 詳しくは、ベンチマークテストのドキュメンテーションを参照されたし。

テスト出力の次の部分、つまりDoc-tests adderで始まる部分は、ドキュメンテーションテストの結果用のものです。 まだドキュメンテーションテストは何もないものの、コンパイラは、APIドキュメントに現れたどんなコード例もコンパイルできます。 この機能により、ドキュメントとコードを同期することができるわけです。ドキュメンテーションテストの書き方については、 第14章の「テストとしてのドキュメンテーションコメント」節で議論しましょう。今は、Doc-tests出力は無視します。

テストの名前を変更してどうテスト出力が変わるか確かめましょう。it_works関数を違う名前、explorationなどに変えてください。 そう、以下のように:

ファイル名: src/lib.rs

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

そして、cargo testを再度走らせます。これで出力がit_worksの代わりにexplorationと表示しています:

running 1 test
test tests::exploration ... ok

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

別のテストを追加しますが、今回は失敗するテストにしましょう!テスト関数内の何かがパニックすると、 テストは失敗します。各テストは、新規スレッドで実行され、メインスレッドが、テストスレッドが死んだと確認した時、 テストは失敗と印づけられます。第9章でパニックを引き起こす最も単純な方法について語りました。 要するに、panic!マクロを呼び出すことです。src/lib.rsファイルがリスト11-3のような見た目になるよう、 新しいテストanotherを入力してください。

ファイル名: src/lib.rs

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

    #[test]
    fn another() {
        //このテストを失敗させる
        panic!("Make this test fail");
    }
}

リスト11-3: panic!マクロを呼び出したために失敗する2番目のテストを追加する

cargo testで再度テストを走らせてください。出力はリスト11-4のようになるはずであり、 explorationテストは通り、anotherは失敗したと表示されます。

running 2 tests
test tests::exploration ... ok
test tests::another ... FAILED

failures:

---- tests::another stdout ----
    thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:10:8
note: Run with `RUST_BACKTRACE=1` for a backtrace.

failures:
    tests::another

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

error: test failed

リスト11-4: 一つのテストが通り、一つが失敗するときのテスト結果

okの代わりにtest test::anotherの行は、FAILEDを表示しています。個々の結果とまとめの間に、 2つ新たな区域ができました: 最初の区域は、失敗したテスト各々の具体的な理由を表示しています。 今回の場合、another'Make this test fail'でパニックしたために失敗し、 これは、src/lib.rsファイルの10行で起きました。次の区域は失敗したテストの名前だけを列挙しています。 これは、テストがたくさんあり、失敗したテストの詳細がたくさん表示されるときに有用になります。 失敗したテストの名前を使用してそのテストだけを実行し、より簡単にデバッグすることができます。 テストの実行方法については、「テストの実行され方を制御する」節でもっと語りましょう。

総括の行が最後に出力されています: 総合的に言うと、テスト結果は失敗でした。 一つのテストが通り、一つが失敗したわけです。

異なる筋書きでのテスト結果がどんな風になるか見てきたので、テストを行う際に有用になるpanic!以外のマクロに目を向けましょう。

assert!マクロで結果を確認する

assert!マクロは、標準ライブラリで提供されていますが、テスト内の何らかの条件がtrueと評価されることを確かめたいときに有効です。 assert!マクロには、論理値に評価される引数を与えます。その値がtrueなら、 assert!は何もせず、テストは通ります。その値がfalseなら、assert!マクロはpanic!マクロを呼び出し、 テストは失敗します。assert!マクロを使用することで、コードが意図した通りに機能していることを確認する助けになるわけです。

第5章のリスト5-15で、Rectangle構造体とcan_holdメソッドを使用しました。リスト11-5でもそれを繰り返しています。 このコードをsrc/lib.rsファイルに放り込み、assert!マクロでそれ用のテストを何か書いてみましょう。

ファイル名: src/lib.rs

# fn main() {}
#[derive(Debug)]
pub struct Rectangle {
    length: u32,
    width: u32,
}

impl Rectangle {
    pub fn can_hold(&self, other: &Rectangle) -> bool {
        self.length > other.length && self.width > other.width
    }
}

リスト11-5: 第5章からRectangle構造体とそのcan_holdメソッドを使用する

can_holdメソッドは論理値を返すので、assert!マクロの完璧なユースケースになるわけです。 リスト11-6で、長さが8、幅が7のRectangleインスタンスを生成し、これが長さ5、 幅1の別のRectangleインスタンスを保持できるとアサーションすることでcan_holdを用いるテストを書きます。

ファイル名: src/lib.rs

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

    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle { length: 8, width: 7 };
        let smaller = Rectangle { length: 5, width: 1 };

        assert!(larger.can_hold(&smaller));
    }
}

リスト11-6: より大きな四角形がより小さな四角形を確かに保持できるかを確認するcan_hold用のテスト

testsモジュール内に新しい行を加えたことに注目してください: use super::*です。 testsモジュールは、第7章の「プライバシー規則」節で講義した通常の公開ルールに従う普通のモジュールです。 testsモジュールは、内部モジュールなので、外部モジュール内のテスト配下にあるコードを内部モジュールのスコープに持っていく必要があります。 ここではglobを使用して、外部モジュールで定義したもの全てがこのtestsモジュールでも使用可能になるようにしています。

テストはlarger_can_hold_smallerと名付け、必要なRectangleインスタンスを2つ生成しています。 そして、assert!マクロを呼び出し、larger.can_hold(&smaller)の呼び出し結果を渡しました。 この式は、trueを返すと考えられるので、テストは通るはずです。確かめましょう!

running 1 test
test tests::larger_can_hold_smaller ... ok

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

確かに!別のテストを追加しましょう。今回は、小さい四角形は、より大きな四角形を保持できないことをアサーションします。

ファイル名: src/lib.rs

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

    #[test]
    fn larger_can_hold_smaller() {
        // --snip--
    }

    #[test]
    fn smaller_cannot_hold_larger() {
        let larger = Rectangle { length: 8, width: 7 };
        let smaller = Rectangle { length: 5, width: 1 };

        assert!(!smaller.can_hold(&larger));
    }
}

今回の場合、can_hold関数の正しい結果はfalseなので、その結果をassert!マクロに渡す前に反転させる必要があります。 結果として、can_holdfalseを返せば、テストは通ります。

running 2 tests
test tests::smaller_cannot_hold_larger ... ok
test tests::larger_can_hold_smaller ... ok

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

通るテストが2つ!さて、コードにバグを導入したらテスト結果がどうなるか確認してみましょう。 長さを比較する大なり記号を小なり記号で置き換えてcan_holdメソッドの実装を変更しましょう:

# fn main() {}
# #[derive(Debug)]
# pub struct Rectangle {
#     length: u32,
#     width: u32,
# }
// --snip--

impl Rectangle {
    pub fn can_hold(&self, other: &Rectangle) -> bool {
        self.length < other.length && self.width > other.width
    }
}

テストを実行すると、以下のような出力をします:

running 2 tests
test tests::smaller_cannot_hold_larger ... ok
test tests::larger_can_hold_smaller ... FAILED

failures:

---- tests::larger_can_hold_smaller stdout ----
    thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed:
    larger.can_hold(&smaller)', src/lib.rs:22:8
    (スレッド'tests::larger_can_hold_smallerはsrc/lib.rs:22:8の'assertion failed: larger.can_hold(&smaller)'
    でパニックしました)
note: Run with `RUST_BACKTRACE=1` for a backtrace.

failures:
    tests::larger_can_hold_smaller

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

テストによりバグが捕捉されました!larger.lengthが8、smaller.lengthが5なので、 can_hold内の長さの比較が今はfalseを返すようになったのです: 8は5より小さくないですからね。

assert_eq!assert_ne!マクロで等値性をテストする

機能をテストする一般的な方法は、テスト下にあるコードの結果をコードが返すと期待される値と比較して、 等しいと確かめることです。これをassertマクロを使用して==演算子を使用した式を渡すことで行うこともできます。 しかしながら、これはありふれたテストなので、標準ライブラリには1組のマクロ(assert_eq!assert_ne!)が提供され、 このテストをより便利に行うことができます。これらのマクロはそれぞれ、二つの引数を等値性と非等値性のために比較します。 また、アサーションが失敗したら二つの値の出力もし、テストが失敗した原因を確認しやすくなります。 一方でassert!マクロは、==式の値がfalse値になったことしか示唆せず、false値に導いた値は出力しません。

リスト11-7において、引数に2を加えて結果を返すadd_twoという名前の関数を書いています。 そして、assert_eq!マクロでこの関数をテストしています。

ファイル名: src/lib.rs

# fn main() {}
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));
    }
}

リスト11-7: assert_eq!マクロでadd_two関数をテストする

テストが通ることを確認しましょう!

running 1 test
test tests::it_adds_two ... ok

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

assert_eq!マクロに与えた第1引数の4は、add_two(2)の呼び出し結果と等しいです。 このテストの行はtest tests::it_adds_two ... okであり、okというテキストはテストが通ったことを示しています!

コードにバグを仕込んで、assert_eq!を使ったテストが失敗した時にどんな見た目になるのか確認してみましょう。 add_two関数の実装を代わりに3を足すように変えてください:

# fn main() {}
pub fn add_two(a: i32) -> i32 {
    a + 3
}

テストを再度実行します:

running 1 test
test tests::it_adds_two ... FAILED

failures:

---- tests::it_adds_two stdout ----
        thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)`
  left: `4`,
 right: `5`', src/lib.rs:11:8
note: Run with `RUST_BACKTRACE=1` for a backtrace.

failures:
    tests::it_adds_two

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

テストがバグを捕捉しました!it_adds_twoのテストは失敗し、assertion failed: `(left == right)`というメッセージを表示し、 left4で、right5だったと示しています。このメッセージは有用で、デバッグを開始する助けになります: assert_eq!left引数は4だったが、add_two(2)があるright引数は5だったことを意味しています。

ある言語やテストフレームワークでは、二つの値が等しいとアサーションを行う関数の引数は、 expectedactualと呼ばれ、引数を指定する順序が問題になることに注意してください。 ですがRustでは、leftrightと呼ばれ、期待する値とテスト下のコードが生成する値を指定する順序は、 問題になりません。assert_eq!(add_two(2), 4)と今回のテストのアサーションを書くこともでき、 そうすると失敗メッセージは、assertion failed: `(left == right)`となり、 left5right4と表示されるわけです。

assert_ne!マクロは、与えた2つの値が等しくなければ通り、等しければ失敗します。 このマクロは、値が何になるだろうか確信が持てないけれども、コードが意図した通りに動いていれば、 確実にこの値にはならないだろうとわかっているような場合に最も有用になります。例えば、 入力を何らかの手段で変えることが保障されているけれども、入力が変更される方法がテストを実行する曜日に依存する関数をテストしているなら、 アサーションすべき最善の事柄は、関数の出力が入力と等しくないことかもしれません。

表面下では、assert_eq!assert_ne!マクロはそれぞれ、==!=演算子を使用しています。 アサーションが失敗すると、これらのマクロは引数をデバッグフォーマットを使用して出力するので、 比較対象の値はPartialEqDebugトレイトを実装していなければなりません。 組み込み型の全部と、標準ライブラリの型はほぼ全てこれらのトレイトを実装しています。 自分で定義した構造体とenumについては、PartialEqを実装して、 その型の値が等しいか等しくないかアサーションする必要があるでしょう。Debugを実装して、 アサーションが失敗した時に値を出力する必要もあるでしょう。 第5章のリスト5-12で触れたように、どちらのトレイトも継承可能トレイトなので、 これは通常、構造体やenum定義に#[derive(PartialEq, Debug)]というアノテーションを追加するくらい率直になります。 これらや他の継承可能トレイトに関する詳細については、付録Cをご覧ください。

カスタムの失敗メッセージを追加する

さらに、assert!assert_eq!assert_ne!の追加引数として、失敗メッセージと共にカスタムのメッセージが表示されるよう、 追加することもできます。assert!の1つの必須引数、 あるいはassert_eq!assert_ne!の2つの必須引数の後に指定された引数はどれもformat!マクロに明け渡されるので、 (format!マクロについては第8章の「+演算子または、format!マクロで連結する」節で議論しました)、 {}プレースホルダーを含むフォーマット文字列とこのプレースホルダーに置き換えられる値を渡すことができます。 カスタムメッセージは、アサーションがどんな意味を持つかドキュメント化するのに役に立ちます; テストが失敗した時、問題が何なのかコードと共により良い考えを持てるでしょう。

例として、人々に名前で挨拶をする関数があり、関数に渡した名前が出力に出現することをテストしたいとしましょう:

ファイル名: src/lib.rs

# fn main() {}
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"));
    }
}

このプログラムの必要事項はまだ合意が得られておらず、挨拶の先頭のHelloというテキストは変わるだろうということは極めて確かです。 要件が変わった時にテストを更新しなくてもよいようにしたいと決定したので、 greeting関数から返る値と正確な等値性を確認するのではなく、出力が入力引数のテキストを含むことをアサーションするだけにします。

greetingnameを含まないように変更してこのコードにバグを仕込み、このテストの失敗がどんな見た目になるのか確かめましょう:

# fn main() {}
pub fn greeting(name: &str) -> String {
    String::from("Hello!")
}

このテストを実行すると、以下のように出力されます:

running 1 test
test tests::greeting_contains_name ... FAILED

failures:

---- tests::greeting_contains_name stdout ----
        thread 'tests::greeting_contains_name' panicked at 'assertion failed:
result.contains("Carol")', src/lib.rs:12:8
note: Run with `RUST_BACKTRACE=1` for a backtrace.

failures:
    tests::greeting_contains_name

この結果は、アサーションが失敗し、どの行にアサーションがあるかを示しているだけです。 より有用な失敗メッセージは今回の場合、greeting関数から得た値を出力することでしょう。 greeting関数から得た実際の値で埋められるプレースホルダーを含むフォーマット文字列からなるカスタムの失敗メッセージを与え、 テスト関数を変更しましょう:

#[test]
fn greeting_contains_name() {
    let result = greeting("Carol");
    assert!(
        result.contains("Carol"),
        //挨拶は名前を含んでいません。値は`{}`でした
        "Greeting did not contain name, value was `{}`", result
    );
}

これでテストを実行したら、より有益なエラーメッセージが得られるでしょう:

---- tests::greeting_contains_name stdout ----
        thread 'tests::greeting_contains_name' panicked at 'Greeting did not
contain name, value was `Hello!`', src/lib.rs:12:8
note: Run with `RUST_BACKTRACE=1` for a backtrace.

実際に得られた値がテスト出力に見られ、起こると想定していたものではなく、 起こったものをデバッグするのに役に立ちます。

should_panicでパニックを確認する

期待する正しい値をコードが返すことを確認することに加えて、想定通りにコードがエラー状態を扱っていることを確認するのも重要です。 例えば、第9章のリスト9-9で生成したGuess型を考えてください。Guessを使用する他のコードは、 Guessのインスタンスは1から100の範囲の値しか含まないという保証に依存しています。 その範囲外の値でGuessインスタンスを生成しようとするとパニックすることを保証するテストを書くことができます。

これは、テスト関数にshould_panicという別の属性を追加することで達成できます。 この属性は、関数内のコードがパニックしたら、テストを通過させます。つまり、 関数内のコードがパニックしなかったら、テストは失敗するわけです。

リスト11-8は、想定した時にGuess::newのエラー条件が発生していることを確認するテストを示しています。

ファイル名: src/lib.rs

# fn main() {}
pub struct Guess {
    value: u32,
}

impl Guess {
    pub fn new(value: u32) -> Guess {
        if value < 1 || value > 100 {
            //予想値は1から100の間でなければなりません
            panic!("Guess value must be between 1 and 100, got {}.", value);
        }

        Guess {
            value
        }
    }
}

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

    #[test]
    #[should_panic]
    fn greater_than_100() {
        Guess::new(200);
    }
}

リスト11-8: 状況がpanic!を引き起こすとテストする

#[test]属性の後、適用するテスト関数の前に#[should_panic]属性を配置しています。 このテストが通るときの結果を見ましょう:

running 1 test
test tests::greater_than_100 ... ok

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

よさそうですね!では、値が100より大きいときにnew関数がパニックするという条件を除去することでコードにバグを導入しましょう:

# fn main() {}
# pub struct Guess {
#     value: u32,
# }
#
// --snip--

impl Guess {
    pub fn new(value: u32) -> Guess {
        if value < 1  {
            panic!("Guess value must be between 1 and 100, got {}.", value);
        }

        Guess {
            value
        }
    }
}

リスト11-8のテストを実行すると、失敗するでしょう:

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]で注釈されていることがわかります。得られた失敗は、 テスト関数のコードがパニックを引き起こさなかったことを意味するのです。

should_panicを使用するテストは不正確なこともあります。なぜなら、コードが何らかのパニックを起こしたことしか示さないからです。 should_panicのテストは、起きると想定していたもの以外の理由でテストがパニックしても通ってしまうのです。 should_panicのテストの正確を期すために、should_panic属性の省略可能なexpected引数を追加できます。 このテストの拘束具が、失敗メッセージに与えられたテキストが含まれていることを確かめてくれるでしょう。 例えば、リスト11-9のGuessの変更されたコードを考えてください。ここでは、 new関数は、値の大小によって異なるメッセージでパニックします。

ファイル名: src/lib.rs

# fn main() {}
# pub struct Guess {
#     value: u32,
# }
#
// --snip--

impl Guess {
    pub fn new(value: u32) -> Guess {
        if value < 1 {
            //予想値は、1以上でなければなりませんが、{}でした
            panic!("Guess value must be greater than or equal to 1, got {}.",
                   value);
        } else if value > 100 {
            //予想値は100以下でなければなりませんが、{}でした
            panic!("Guess value must be less than or equal to 100, got {}.",
                   value);
        }

        Guess {
            value
        }
    }
}

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

    #[test]
    // 予想値は100以下でなければなりません
    #[should_panic(expected = "Guess value must be less than or equal to 100")]
    fn greater_than_100() {
        Guess::new(200);
    }
}

リスト11-9: 状況が特定のパニックメッセージでpanic!を引き起こすことをテストする

should_panic属性のexpected引数に置いた値がGuess::new関数がパニックしたメッセージの一部になっているので、 このテストは通ります。予想されるパニックメッセージ全体を指定することもでき、今回の場合、 Guess value must be less than or equal to 100, got 200.となります。 should_panicの予想される引数に指定すると決めたものは、パニックメッセージの固有性や活動性、 テストの正確性によります。今回の場合、パニックメッセージの一部でも、テスト関数内のコードが、 else if value > 100ケースを実行していると確認するのに事足りるのです。

expectedメッセージありのshould_panicテストが失敗すると何が起きるのが確かめるために、 if value < 1else if value > 100ブロックの本体を入れ替えることで再度コードにバグを仕込みましょう:

if value < 1 {
    panic!("Guess value must be less than or equal to 100, got {}.", value);
} else if value > 100 {
    panic!("Guess value must be greater than or equal to 1, got {}.", value);
}

should_panicテストを実行すると、今回は失敗するでしょう:

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

failures:

---- tests::greater_than_100 stdout ----
        thread 'tests::greater_than_100' panicked at 'Guess value must be
greater than or equal to 1, got 200.', src/lib.rs:11:12
note: Run with `RUST_BACKTRACE=1` for a backtrace.
note: Panic did not include expected string 'Guess value must be less than or
equal to 100'
(注釈: パニックには'Guess value must be less than or equal to 100'という予想される文字列が含まれませんでした)

failures:
    tests::greater_than_100

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

この失敗メッセージは、このテストが確かにまさしく予想通りパニックしたことを示唆していますが、 パニックメッセージは、予想される文字列の'Guess value must be less than or equal to 100'を含んでいませんでした。 実際に得られたパニックメッセージは今回の場合、Guess value must be greater than or equal to 1, got 200でした。 そうしてバグの所在地を割り出し始めることができるわけです!

今やテスト記法を複数知ったので、テストを走らせる際に起きていることに目を向け、 cargo testで使用できるいろんなオプションを探求しましょう。