Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

テスト

Rustの組み込みテスト機能を学びます。

単体テスト

基本的なテスト

#![allow(unused)]
fn main() {
fn add(a: i32, b: i32) -> i32 {
    a + b
}

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

    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    fn test_add_negative() {
        assert_eq!(add(-1, 1), 0);
    }
}
}

実行:

cargo test

アサーションマクロ

#![allow(unused)]
fn main() {
#[test]
fn test_assertions() {
    // 等値チェック
    assert_eq!(1 + 1, 2);

    // 不等値チェック
    assert_ne!(1 + 1, 3);

    // 条件チェック
    assert!(true);
    assert!(!false);

    // カスタムメッセージ
    assert_eq!(1 + 1, 2, "1 + 1 は 2 のはず");
}
}

パニックのテスト

#![allow(unused)]
fn main() {
fn divide(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("ゼロ除算!");
    }
    a / b
}

#[test]
#[should_panic]
fn test_divide_by_zero() {
    divide(10, 0);
}

#[test]
#[should_panic(expected = "ゼロ除算")]
fn test_divide_by_zero_message() {
    divide(10, 0);
}
}

Result を返すテスト

#![allow(unused)]
fn main() {
#[test]
fn test_with_result() -> Result<(), String> {
    if 2 + 2 == 4 {
        Ok(())
    } else {
        Err(String::from("計算が間違っています"))
    }
}
}

テストの構成

テストモジュール

#![allow(unused)]
fn main() {
// src/lib.rs
pub fn public_function() -> i32 {
    private_function() + 1
}

fn private_function() -> i32 {
    42
}

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

    #[test]
    fn test_public() {
        assert_eq!(public_function(), 43);
    }

    #[test]
    fn test_private() {
        // 同じモジュール内なのでプライベート関数もテスト可能
        assert_eq!(private_function(), 42);
    }
}
}

テストのフィルタリング

# すべてのテスト
cargo test

# 特定のテストのみ
cargo test test_add

# 特定のモジュールのテスト
cargo test tests::

# 無視されたテストを実行
cargo test -- --ignored

テストの無視

#![allow(unused)]
fn main() {
#[test]
#[ignore]
fn expensive_test() {
    // 時間のかかるテスト
}
}

統合テスト

プロジェクト全体をテストします。

my_project/
├── src/
│   └── lib.rs
└── tests/
    └── integration_test.rs

tests/integration_test.rs

#![allow(unused)]
fn main() {
use my_project;

#[test]
fn test_integration() {
    assert_eq!(my_project::public_function(), 43);
}
}

テストのベストプラクティス

1. AAAパターン

#![allow(unused)]
fn main() {
#[test]
fn test_user_creation() {
    // Arrange(準備)
    let name = "太郎";
    let age = 25;

    // Act(実行)
    let user = User::new(name.to_string(), age);

    // Assert(検証)
    assert_eq!(user.name, "太郎");
    assert_eq!(user.age, 25);
}
}

2. 境界値テスト

#![allow(unused)]
fn main() {
#[test]
fn test_is_adult() {
    assert!(!is_adult(17));  // 境界の下
    assert!(is_adult(18));   // 境界
    assert!(is_adult(19));   // 境界の上
}
}

3. エラーケースのテスト

#![allow(unused)]
fn main() {
#[test]
fn test_parse_error() {
    let result = parse_number("not a number");
    assert!(result.is_err());
}

#[test]
fn test_parse_success() {
    let result = parse_number("42");
    assert_eq!(result.unwrap(), 42);
}
}

テストヘルパー

共通のセットアップ

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    fn setup() -> User {
        User::new("テスト太郎".to_string(), 20)
    }

    #[test]
    fn test_user_name() {
        let user = setup();
        assert_eq!(user.name, "テスト太郎");
    }

    #[test]
    fn test_user_age() {
        let user = setup();
        assert_eq!(user.age, 20);
    }
}
}

テストカバレッジ

# cargo-tarpaulinをインストール
cargo install cargo-tarpaulin

# カバレッジレポート
cargo tarpaulin

まとめ

機能説明
#[test]テスト関数を示す
assert!条件がtrueか検証
assert_eq!等値を検証
assert_ne!不等値を検証
#[should_panic]パニックを期待
#[ignore]テストを無視
#[cfg(test)]テスト時のみコンパイル

確認テスト

Q1. `assert_eq!(a, b)`と`assert_ne!(a, b)`の違いは?

Q2. `#[should_panic]`の用途は?

Q3. 以下のテストの結果は?
#[test] fn test() { assert_eq!(2 + 2, 5); }

Q4. `assert!(is_even(3))`が失敗する理由は?(is_evenは偶数でtrueを返す関数)

Q5. 統合テストを配置する正しいディレクトリは?


次のドキュメント: 04_documentation.md