🧪

[Rust] 単体テスト、結合テストの書き方

2024/08/23に公開

rust でテストを書く時のファイル構成とモジュールの関係について。
mockついては書きません。

単体テストの書き方

rust で単体テストを書く場合はテストしたいコードとテストコードをまとめて一つのファイルに書くというのが、rustのイディオムということらしいです。

lib.rs
fn f() -> i32 {
    1
}
#[cfg(test)]
mod tests{
    use super::*;
    #[test]
    fn should_return_1()
    {
        assert_eq!(f(),1);
    }
}

mod testsで宣言されたモジュールは子のモジュールとして扱われます。そして子であるtestsは親のprivate な関数を実行できます。

テストを別のファイルに書きたい場合

ファイルを分けたい場合は新たにディレクトリを作り、その階層にmod.rstest.rsを用意することで実現できます。
ここでいうtest.rsの名前は何でもよいです。ファイルの名前がモジュールの名前になります。
そしてmod.rs内でtest.rsをモジュールとして認識するように指定します。

mod.rs
#[cfg(test)]
mod test;

// テストしたいコード
fn f() -> i32 {
    1
}
test.rs
use super::*;
#[test]
fn should_return_1() {
    assert_eq!(f() , 1);
}
lib.rs
mod foo;

ディレクトリ構成はこのようになります。
fooというフォルダ名がモジュールの名前になります。

.
└── project/
    ├── src/
    │   ├── foo/
    │   │   ├── mod.rs
    │   │   └── test.rs
    │   └── lib.rs
    ├── tests/
    │   └── integration_test.rs
    └── Cargo.toml

ポイントとしてはmod.rstest.rsは同じ階層にありますが、親子関係であるということです。
ここで同じ階層に新たにfoo_2.rsというファイルを追加した場合はどうなるでしょうか。
そのモジュールもmod.rsの子になります。そしてtest.rsfoo_2.rsは兄弟という関係になります。
test.rsから兄弟であるfoo_2.rsのprivateな関数を呼ぶことはできません。その逆も。
つまり、子のモジュールは親のprivate関数を呼ぶことができますが、兄弟のprivate関数を呼ぶことができません。

.
└── src/
    ├── foo/
    │   ├── mod.rs    // 親
    │   ├── test.rs   // mod.rsの子 foo_2.rsと兄弟
    │   └── foo_2.rs  // mod.rsの子 test.rsと兄弟
    └── lib.rs

結合テストの書き方

プロジェクトのrootにtestsディレクトリを作ります。rustにおいて、rootにあるtestsディレクトリ(Cargo.tomlと同じ階層にある)はプロジェクトとは別のcrateとして扱わるのでバイナリクレートを作っている場合でも、結合テストを書く場合には外部に公開するためのlib.rsが必要になります。
また、testsからはpivate関数を呼ぶことはできません。

└── project
    ├── src/
    │   ├── main.rs
    │   └── lib.rs    //必要
    ├── tests/
    │   └── integration_test.rs
    ├── Cargo.toml

まとめ

単体テストの場合はxx.rsの中にテストモジュールを直接定義するという方法と、mod.rstest.rsを用意してファイルを分ける方法があります。
個人的には一つのファイルにまとめて書く方がディレクトリがスッキリして好きです。
結合テストはtestsディレクトリの中に書きましょう。

Discussion