Rustのドキュメントテストとは?
Rustには ドキュメントテスト(doctest) という便利な仕組みがあります。コードのコメント(/// で書くドキュメントコメント)に記載したコードブロックを、そのままテストとして実行できます。
「ドキュメントが古くなってコードと食い違う」という問題はどのプロジェクトでも起きがちです。doctestを使えば、ドキュメントのコード例が常に動作することを自動的に保証できます。
基本的な書き方
関数に /// コメントを付け、その中に ```rust のコードブロックを書くだけです。
/// 2つの整数を足し合わせて返す。
///
/// # Examples
///
/// ```
/// let result = my_crate::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
cargo test を実行すると、通常のユニットテストと一緒にdoctestも走ります。
$ cargo test
running 1 test
test src/lib.rs - add (line 5) ... ok
test result: ok. 1 passed; 0 failed
# Examples という見出しは必須ではありませんが、慣習的に付けることが推奨されています。
よく使う記法
パニックを期待するテスト
should_panic アノテーションを使うと、コードがパニックすることを期待するテストを書けます。
/// ゼロ除算を行うとパニックする。
///
/// ```should_panic
/// my_crate::divide(10, 0);
/// ```
pub fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("ゼロで割ることはできません");
}
a / b
}
コンパイルエラーを期待するテスト
compile_fail を使うと、そのコードがコンパイルエラーになることを確認できます。型制約の説明に便利です。
/// この関数はi32のみ受け付ける。
///
/// ```compile_fail
/// my_crate::double("hello"); // 文字列は渡せない
/// ```
pub fn double(x: i32) -> i32 {
x * 2
}
テストとして実行しないコードブロック
表示用のサンプルコードでテストを走らせたくない場合は no_run を使います。
/// ファイルを読み込む例(実行環境依存のため実行しない)。
///
/// ```no_run
/// let content = std::fs::read_to_string("data.txt").unwrap();
/// println!("{}", content);
/// ```
pub fn read_file(path: &str) -> String {
std::fs::read_to_string(path).unwrap()
}
# でコード行を非表示にする
doctestではコンテキスト設定のコードが必要でも、読者に見せたくない場合があります。行頭に # を付けると、テストには含まれるがドキュメントには表示されません。
/// Configを使ってリクエストを処理する。
///
/// ```
/// # use my_crate::Config;
/// # let config = Config::default();
/// let result = config.process("data");
/// assert_eq!(result, "processed: data");
/// ```
pub struct Config {
pub prefix: String,
}
impl Config {
pub fn default() -> Self {
Config { prefix: "processed".to_string() }
}
pub fn process(&self, input: &str) -> String {
format!("{}: {}", self.prefix, input)
}
}
# use my_crate::Config; などのuse文は画面上には表示されず、テスト実行時だけ有効になります。
モジュールレベルのdoctest
//! を使ったモジュールコメントにもdoctestを書けます。クレート全体の使い方を説明するサンプルとして活用できます。
//! # my_crate
//!
//! シンプルな計算ユーティリティ。
//!
//! ## 使い方
//!
//! ```
//! use my_crate::{add, divide};
//!
//! assert_eq!(add(1, 2), 3);
//! assert_eq!(divide(10, 2), 5);
//! ```
doctestだけを実行する
--doc オプションを付けるとdoctestのみを実行できます。開発中に素早く確認したいときに便利です。
$ cargo test --doc
逆に、doctestをスキップして通常のテストだけ実行するには次のようにします。
$ cargo test --lib
実践的なアドバイス
doctestはシンプルなケースに最も向いています。複雑なセットアップが必要なロジックは、通常の #[test] 関数に任せるのがベターです。
| 用途 | 向いているテスト |
|---|---|
| 公開APIの使い方を示す | doctest |
| 複雑な内部ロジックの検証 | #[test] |
| ファイルI/OやDBを使う処理 | #[test] + モック |
| エラーケースの網羅的なチェック | #[test] |
まとめ
///コメント内の```rust ```ブロックは自動的にテストになるshould_panic/compile_fail/no_runでテストの挙動を制御できる#を行頭に付けると表示せずにテストコードを書けるcargo test --docでdoctestだけを実行できる
ドキュメントとテストを同時に管理できるdoctestは、Rustならではの強力な機能です。公開APIを持つライブラリを作るときは、ぜひ積極的に活用してみてください。