あっぽログ
← 記事一覧に戻る

RustのDocテストを使いこなす:コードコメントを自動テストに変える実践ガイド

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を持つライブラリを作るときは、ぜひ積極的に活用してみてください。

← 記事一覧に戻る