Rustでcargo testを行った時、何のテストが実行されるか

  • Rustのテストに関して調べていた時に気になったので実験してみました。

例1_main.rsとlib.raがある状態

ディレクトリ構成とコード

calc_test
  ├ src
  │ ├ main.rs
  │ └ lib.rs
  ├ tests
  │ └ i_test.rs
  └ Cargo.toml

上のような構成です。続いてはコードです。(関数名が雑なのは実験ということでご容赦ください)

src/main.rs

mod lib;

fn main() {
    let num = 5;
    println!("{} * 2 = {}", num, lib::mul_two(num));
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn mul_works() {
        assert_eq!(4, lib::mul_two(2));
    }
}

src/lib.rs

/// ```
/// use calc_test::mul_two;
///
/// assert_eq!(2, mul_two(1));
/// ```
pub fn mul_two(num: u32) -> u32 {
    add(num, num)
}

fn add(a: u32, b: u32) -> u32 {
    a + b
}

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

    #[test]
    fn three_times_two() {
        assert_eq!(6, mul_two(3));
    }
    #[test]
    fn add_two_and_two() {
        assert_eq!(4, add(2, 2));
    }
}

tests/i_test.rs

#[test]
fn test_mul() {
    assert_eq!(4, calc_test::mul_two(2));
}

結果

cargo testを実行すると以下のような出力が得られます。

running 2 tests
test tests::add_two_and_two ... ok
test tests::three_times_two ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s  

     Running unittests src\main.rs (target\debug\deps\calc_test-194625f6c7806948.exe)

running 3 tests
test lib::tests::add_two_and_two ... ok
test lib::tests::three_times_two ... ok
test tests::mul_works ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s  

     Running tests\i_test.rs (target\debug\deps\i_test-879837980f56faa5.exe)

running 1 test
test test_mul ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s  

   Doc-tests calc_test

running 1 test
test src\lib.rs - mul_two (line 1) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.52s  
  • 下記4つのテストが実行されました。
    1. lib.rsから辿れるファイルのunittest
    2. main.rsから辿れるファイルのunittest
    3. ドキュメントテスト
    4. (testsディレクトリがある場合)testディレクトリ内のファイルのtest

例2_lib.rsがない状態

ディレクトリ構成とコード

calc_test
  ├ src
  │ ├ main.rs
  │ └ calc.rs
  ├ tests
  │ └ i_test.rs
  └ Cargo.toml

lib.rsの名前をcalc.rsに変えました。それに伴い、main.rs内を以下のように変えてます。

  • mod lib; -> mod calc;
  • lib::~; -> calc::~

結果

cargo testを実行するとエラーになります。

error[E0433]: failed to resolve: use of undeclared crate or module `calc_test`
 --> tests\i_test.rs:3:19
  |
3 |     assert_eq!(4, calc_test::mul_two(2));
  |                   ^^^^^^^^^ use of undeclared crate or module `calc_test`

これは、testsディレクトリ以下で作成する結合テスト

ライブラリ(つまり`lib.rs)を外側からテストするための機能

としてあるからだと思われます。そのためlib.rsがないと、いくらmain.rsがあってもmul_two関数を見つけられずにエラーが出るのだと思われます。

これはThe Rust Programming Language 日本語版にも言及があります。

これは、バイナリを提供するRustのプロジェクトに、 src/lib.rsファイルに存在するロジックを呼び出す単純なsrc/main.rsファイルがある一因になっています。 この構造を使用して結合テストは、extern crateを使用して重要な機能を用いることで ライブラリクレートをテストすることができます。 この重要な機能が動作すれば、src/main.rsファイルの少量のコードも動作し、 その少量のコードはテストする必要がないわけです。

(まぁ、これが本当かどうかを見たかったのが今回の記事のきっかけなんですけどね、笑)

追実験_i_test.rsを通るようにしてみた場合

さて、では今度はi_test.rsを以下のようにエラーにならないようにしてみます。

#[test]
fn test_mul() {
    assert_eq!(4, 2 + 2);
    //assert_eq!(4, calc_test::mul_two(2));
}

この状態でcargo testを実行した結果は以下の通りです。

running 3 tests
test calc::tests::three_times_two ... ok
test calc::tests::add_two_and_two ... ok
test tests::mul_works ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s  

     Running tests\i_test.rs (target\debug\deps\i_test-6157c0f04bfd7328.exe)

running 1 test
test test_mul ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s  

例1で書いた4つのテストのうち、2.の「main.rsから辿れるファイルのunittest」と4.の「(testsディレクトリがある場合)testディレクトリ内のファイルのtest」が実行されています。

1.の「lib.rsから辿れるファイルのunittest」は当然として、3.の「ドキュメントテスト」が実施されないのは少し意外でした。

ちなみに、なんでlib.rsがなくても結合テストは実施されるようになっているんだろうか、、、 lib.rsがあることが前提なら、lib.rsが無い時は結合テストは無視してくれても良さそうな気がするけど、、、

おわりに

  • いろいろな条件でcargo testを実行した時にどんな出力が得られるのか確認してみました。
  • 結合テストlib.rsがあることが前提になっているみたいですので、ディレクトリ構成には注意が必要です。
  • ドキュメントテストもlib.rsがないと実行はされないみたいですのでこれも注意が必要です。