S-JIS[2024-12-14/2025-02-01] 変更履歴

Rustのテスト

Rustのテスト方法のメモ。


概要

Rustでは、テストコードを記述・実行することが出来る。
(JavaのJUnit相当)

Rustでテストコードを書く場所は、テスト対象のソースファイルの末尾にするのが一般的なようだ。


テストの記述方法

ソースファイルの末尾にテスト用のモジュールを作る。
テスト用のモジュール名は何でもいいようだが、慣例としてtestやtestsとする。

テスト用モジュールには#[cfg(test)]属性を付ける。
それぞれのテスト関数には#[test]属性を付ける。

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

    #[test]
    fn テスト関数1() {
        〜
    }

    #[test]
    fn テスト関数2() {
        〜
    }

    …
}

テストのみで使用したい構造体や関数等は、(テスト対象コードのuse文とは別に、)テスト用モジュールのブロックの中でuse文を記述してインポートする必要がある。
大抵はテスト対象の構造体や関数を呼び出すだろうから、「use super::*」でインポートしておくと便利。
(テストコードはテスト用モジュールにあり、テスト対象とは異なるモジュールとなるので、テスト対象をインポートする必要がある)

テスト関数の戻り型を指定することは出来ない。(Resultに出来ると便利なのだが…)
テスト関数の中身は、処理を実行して、結果をassert_eq!マクロ等で確認することになるだろう。

テストが実行される順序は不定。(しかも、cargo testのデフォルトでは、複数スレッドで並列実行される)

テスト全体の前処理や後処理を記述することは出来ないので、各テストで完結する必要がある。


非同期関数のテスト方法

非同期処理(asyncのついた関数)を呼び出してテストしたい場合は、tokio::testを使う。

    #[tokio::test]
    async fn 非同期テスト() {
         〜awaitを使った非同期処理呼び出し〜
    }
    use tokio::test;

    #[test]
    async fn 非同期テスト() {
         〜awaitを使った非同期処理呼び出し〜
    }

テストの実行方法

テストはCargoを使って実行する。

cargo test 〔オプション〕〔テスト名〕〔-- 引数…〕

テスト名を指定すると、その名前を含むテストが実行される。(テスト名は、テスト関数が属しているモジュール名も含む)
テスト名を省略すると、全テストが実行される。

WindowsのコマンドプロンプトやLinux(bash)では、テスト名に「""」を指定すると全テストが実行される。
(WindowsのPowerShellでは「""」は引数として認識してくれない模様)

ヘルプによると、「--」の後ろに引数が書けることになっている(テスト名は「--」の前に指定するように見える)が、実際はハイフン「-」で始まらない一番最初の引数がテスト名として扱われるようだ。


標準出力を出力する方法

テスト関数の中でprintln!やeprintln!マクロによって出力されたメッセージは、 デフォルトではテスト実行時に表示されない。

「--」の後ろに「--nocapture」を付けると、メッセージがコンソールに出力されるようになる。

cargo test 〔オプション〕〔テスト名〕 -- --nocapture

スレッド数を指定する方法

cargo testのデフォルトでは、テストは複数スレッドで実行される。[2025-02-01]

「--」の後ろに「--test-threads=スレッド数」を付けると、スレッド数が指定できる。
1にすれば1スレッドで実行される。

cargo test 〔オプション〕〔テスト名〕 -- --test-threads=1

テストで引数を受け取る方法

テスト関数の中でstd::env::args()を使って、cargo testの引数を受け取ることが出来る。
cargo testコマンド関連以外の引数が全部取得できるので、自前のキーワードを定義してそれを探すようにすれば、独自のパラメーターを受け渡すことが出来る。

    #[test]
    fn arg_test() {
        let args: Vec<String> = std::env::args().collect();
        let my_arg /*Option<String>*/ = args
            .iter()
            .find(|arg| arg.starts_with("my_arg="))
            .and_then(|arg| arg.split_once('=').map(|(_, value)| value.to_string()));

        println!("my_arg={:?}", my_arg);
    }
cargo test "" -- --nocapture my_arg=abc

Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま