Source profileQuality 78/100Review permissions

affaan-m/ECC/docs/ja-JP/skills/rust-testing/SKILL.md

rust-testing

Use it for testing tasks; the detail page covers purpose, installation, and practical steps.

Source repository stars
234,327
Declared platforms
0
Static risk flags
1
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

TDD方法論に従って信頼性が高く保守しやすいテストを書くための包括的なRustテストパターン。

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "docs/ja-JP/skills/rust-testing"
    Safe inspection promptEditorial

    Inspect the Agent Skill "rust-testing" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/ja-JP/skills/rust-testing/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      使用場面

      新しいRustの関数、メソッド、またはトレイトを書く場合 既存のコードにテストカバレッジを追加する場合 パフォーマンスクリティカルなコードのベンチマークを作成する場合 入力検証にプロパティベーステストを実装する場合 RustプロジェクトでTDDワークフローに従う場合

      新しいRustの関数、メソッド、またはトレイトを書く場合既存のコードにテストカバレッジを追加する場合パフォーマンスクリティカルなコードのベンチマークを作成する場合
    2. 02

      動作原理

      1. ターゲットコードを特定する — テストする関数、トレイト、またはモジュールを見つける 2. テストを書く — [cfg(test)] モジュール内で [test] を使用、rstest でパラメータ化テスト、または proptest でプロパティベーステスト 3. 依存関係をモックする — mockall を使用してテスト対象のユニットを分離する 4. テストを実行する (RED) — テストが期待通りに失敗することを確認する 5. 実装する (GREEN) — テストを通過するための最小限のコードを書く 6. リファクタリングする — テストを…

      ターゲットコードを特定する — テストする関数、トレイト、またはモジュールを見つけるテストを書く — [cfg(test)] モジュール内で [test] を使用、rstest でパラメータ化テスト、または proptest でプロパティベーステスト依存関係をモックする — mockall を使用してテスト対象のユニットを分離する
    3. 03

      RustのTDDワークフロー

      Review the “RustのTDDワークフロー” section in the pinned source before continuing.

      Review and apply the “RustのTDDワークフロー” source section.
    4. 04

      RED-GREEN-REFACTOR サイクル

      Review the “RED-GREEN-REFACTOR サイクル” section in the pinned source before continuing.

      Review and apply the “RED-GREEN-REFACTOR サイクル” source section.
    5. 05

      Rustでの段階的TDD

      Review the “Rustでの段階的TDD” section in the pinned source before continuing.

      Review and apply the “Rustでの段階的TDD” source section.

    Permission review

    Static risk signals and limitations

    Runs scripts

    medium · line 421

    The documentation asks the agent to run terminal commands or scripts.

    cargo llvm-cov # Summary

    Runs scripts

    medium · line 422

    The documentation asks the agent to run terminal commands or scripts.

    cargo llvm-cov --html # HTML report

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score78/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    docs/ja-JP/skills/rust-testing/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Rust テストパターン

    TDD方法論に従って信頼性が高く保守しやすいテストを書くための包括的なRustテストパターン。

    使用場面

    • 新しいRustの関数、メソッド、またはトレイトを書く場合
    • 既存のコードにテストカバレッジを追加する場合
    • パフォーマンスクリティカルなコードのベンチマークを作成する場合
    • 入力検証にプロパティベーステストを実装する場合
    • RustプロジェクトでTDDワークフローに従う場合

    動作原理

    1. ターゲットコードを特定する — テストする関数、トレイト、またはモジュールを見つける
    2. テストを書く#[cfg(test)] モジュール内で #[test] を使用、rstest でパラメータ化テスト、または proptest でプロパティベーステスト
    3. 依存関係をモックする — mockall を使用してテスト対象のユニットを分離する
    4. テストを実行する (RED) — テストが期待通りに失敗することを確認する
    5. 実装する (GREEN) — テストを通過するための最小限のコードを書く
    6. リファクタリングする — テストを通過したまま、コードを改善する
    7. カバレッジを確認する — cargo-llvm-cov を使用し、80%以上を目標にする

    RustのTDDワークフロー

    RED-GREEN-REFACTOR サイクル

    RED     → まず失敗するテストを書く
    GREEN   → テストを通過する最小限のコードを書く
    REFACTOR → テストを通過したままコードをリファクタリングする
    REPEAT  → 次の要件に進む
    

    Rustでの段階的TDD

    // RED: Write test first, use todo!() as placeholder
    pub fn add(a: i32, b: i32) -> i32 { todo!() }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn test_add() { assert_eq!(add(2, 3), 5); }
    }
    // cargo test → panics at 'not yet implemented'
    
    // GREEN: Replace todo!() with minimal implementation
    pub fn add(a: i32, b: i32) -> i32 { a + b }
    // cargo test → PASS, then REFACTOR while keeping tests green
    

    単体テスト

    モジュールレベルのテスト整理

    // src/user.rs
    pub struct User {
        pub name: String,
        pub email: String,
    }
    
    impl User {
        pub fn new(name: impl Into<String>, email: impl Into<String>) -> Result<Self, String> {
            let email = email.into();
            if !email.contains('@') {
                return Err(format!("invalid email: {email}"));
            }
            Ok(Self { name: name.into(), email })
        }
    
        pub fn display_name(&self) -> &str {
            &self.name
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn creates_user_with_valid_email() {
            let user = User::new("Alice", "alice@example.com").unwrap();
            assert_eq!(user.display_name(), "Alice");
            assert_eq!(user.email, "alice@example.com");
        }
    
        #[test]
        fn rejects_invalid_email() {
            let result = User::new("Bob", "not-an-email");
            assert!(result.is_err());
            assert!(result.unwrap_err().contains("invalid email"));
        }
    }
    

    アサーションマクロ

    assert_eq!(2 + 2, 4);                                    // Equality
    assert_ne!(2 + 2, 5);                                    // Inequality
    assert!(vec![1, 2, 3].contains(&2));                     // Boolean
    assert_eq!(value, 42, "expected 42 but got {value}");    // Custom message
    assert!((0.1_f64 + 0.2 - 0.3).abs() < f64::EPSILON);   // Float comparison
    

    エラーとパニックのテスト

    Result の戻り値のテスト

    #[test]
    fn parse_returns_error_for_invalid_input() {
        let result = parse_config("}{invalid");
        assert!(result.is_err());
    
        // Assert specific error variant
        let err = result.unwrap_err();
        assert!(matches!(err, ConfigError::ParseError(_)));
    }
    
    #[test]
    fn parse_succeeds_for_valid_input() -> Result<(), Box<dyn std::error::Error>> {
        let config = parse_config(r#"{"port": 8080}"#)?;
        assert_eq!(config.port, 8080);
        Ok(()) // Test fails if any ? returns Err
    }
    

    パニックのテスト

    #[test]
    #[should_panic]
    fn panics_on_empty_input() {
        process(&[]);
    }
    
    #[test]
    #[should_panic(expected = "index out of bounds")]
    fn panics_with_specific_message() {
        let v: Vec<i32> = vec![];
        let _ = v[0];
    }
    

    統合テスト

    ファイル構造

    my_crate/
    ├── src/
    │   └── lib.rs
    ├── tests/              # 統合テスト
    │   ├── api_test.rs     # 各ファイルが独立したテストバイナリ
    │   ├── db_test.rs
    │   └── common/         # 共有テストユーティリティ
    │       └── mod.rs
    

    統合テストの書き方

    // tests/api_test.rs
    use my_crate::{App, Config};
    
    #[test]
    fn full_request_lifecycle() {
        let config = Config::test_default();
        let app = App::new(config);
    
        let response = app.handle_request("/health");
        assert_eq!(response.status, 200);
        assert_eq!(response.body, "OK");
    }
    

    非同期テスト

    Tokioの使用

    #[tokio::test]
    async fn fetches_data_successfully() {
        let client = TestClient::new().await;
        let result = client.get("/data").await;
        assert!(result.is_ok());
        assert_eq!(result.unwrap().items.len(), 3);
    }
    
    #[tokio::test]
    async fn handles_timeout() {
        use std::time::Duration;
        let result = tokio::time::timeout(
            Duration::from_millis(100),
            slow_operation(),
        ).await;
    
        assert!(result.is_err(), "should have timed out");
    }
    

    テスト整理パターン

    rstest を使用したパラメータ化テスト

    use rstest::{rstest, fixture};
    
    #[rstest]
    #[case("hello", 5)]
    #[case("", 0)]
    #[case("rust", 4)]
    fn test_string_length(#[case] input: &str, #[case] expected: usize) {
        assert_eq!(input.len(), expected);
    }
    
    // Fixtures
    #[fixture]
    fn test_db() -> TestDb {
        TestDb::new_in_memory()
    }
    
    #[rstest]
    fn test_insert(test_db: TestDb) {
        test_db.insert("key", "value");
        assert_eq!(test_db.get("key"), Some("value".into()));
    }
    

    テストヘルパー関数

    #[cfg(test)]
    mod tests {
        use super::*;
    
        /// Creates a test user with sensible defaults.
        fn make_user(name: &str) -> User {
            User::new(name, &format!("{name}@test.com")).unwrap()
        }
    
        #[test]
        fn user_display() {
            let user = make_user("alice");
            assert_eq!(user.display_name(), "alice");
        }
    }
    

    proptest を使用したプロパティベーステスト

    基本的なプロパティテスト

    use proptest::prelude::*;
    
    proptest! {
        #[test]
        fn encode_decode_roundtrip(input in ".*") {
            let encoded = encode(&input);
            let decoded = decode(&encoded).unwrap();
            assert_eq!(input, decoded);
        }
    
        #[test]
        fn sort_preserves_length(mut vec in prop::collection::vec(any::<i32>(), 0..100)) {
            let original_len = vec.len();
            vec.sort();
            assert_eq!(vec.len(), original_len);
        }
    
        #[test]
        fn sort_produces_ordered_output(mut vec in prop::collection::vec(any::<i32>(), 0..100)) {
            vec.sort();
            for window in vec.windows(2) {
                assert!(window[0] <= window[1]);
            }
        }
    }
    

    カスタムストラテジー

    use proptest::prelude::*;
    
    fn valid_email() -> impl Strategy<Value = String> {
        ("[a-z]{1,10}", "[a-z]{1,5}")
            .prop_map(|(user, domain)| format!("{user}@{domain}.com"))
    }
    
    proptest! {
        #[test]
        fn accepts_valid_emails(email in valid_email()) {
            assert!(User::new("Test", &email).is_ok());
        }
    }
    

    mockall を使用したモック

    トレイトベースのモック

    use mockall::{automock, predicate::eq};
    
    #[automock]
    trait UserRepository {
        fn find_by_id(&self, id: u64) -> Option<User>;
        fn save(&self, user: &User) -> Result<(), StorageError>;
    }
    
    #[test]
    fn service_returns_user_when_found() {
        let mut mock = MockUserRepository::new();
        mock.expect_find_by_id()
            .with(eq(42))
            .times(1)
            .returning(|_| Some(User { id: 42, name: "Alice".into() }));
    
        let service = UserService::new(Box::new(mock));
        let user = service.get_user(42).unwrap();
        assert_eq!(user.name, "Alice");
    }
    
    #[test]
    fn service_returns_none_when_not_found() {
        let mut mock = MockUserRepository::new();
        mock.expect_find_by_id()
            .returning(|_| None);
    
        let service = UserService::new(Box::new(mock));
        assert!(service.get_user(99).is_none());
    }
    

    ドキュメントテスト

    実行可能なドキュメント

    /// Adds two numbers together.
    ///
    /// # Examples
    ///
    /// ```
    /// use my_crate::add;
    ///
    /// assert_eq!(add(2, 3), 5);
    /// assert_eq!(add(-1, 1), 0);
    /// ```
    pub fn add(a: i32, b: i32) -> i32 {
        a + b
    }
    
    /// Parses a config string.
    ///
    /// # Errors
    ///
    /// Returns `Err` if the input is not valid TOML.
    ///
    /// ```no_run
    /// use my_crate::parse_config;
    ///
    /// let config = parse_config(r#"port = 8080"#).unwrap();
    /// assert_eq!(config.port, 8080);
    /// ```
    ///
    /// ```no_run
    /// use my_crate::parse_config;
    ///
    /// assert!(parse_config("}{invalid").is_err());
    /// ```
    pub fn parse_config(input: &str) -> Result<Config, ParseError> {
        todo!()
    }
    

    Criterionを使用したベンチマーク

    # Cargo.toml
    [dev-dependencies]
    criterion = { version = "0.5", features = ["html_reports"] }
    
    [[bench]]
    name = "benchmark"
    harness = false
    
    // benches/benchmark.rs
    use criterion::{black_box, criterion_group, criterion_main, Criterion};
    
    fn fibonacci(n: u64) -> u64 {
        match n {
            0 | 1 => n,
            _ => fibonacci(n - 1) + fibonacci(n - 2),
        }
    }
    
    fn bench_fibonacci(c: &mut Criterion) {
        c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
    }
    
    criterion_group!(benches, bench_fibonacci);
    criterion_main!(benches);
    

    テストカバレッジ

    カバレッジの実行

    # Install: cargo install cargo-llvm-cov (or use taiki-e/install-action in CI)
    cargo llvm-cov                    # Summary
    cargo llvm-cov --html             # HTML report
    cargo llvm-cov --lcov > lcov.info # LCOV format for CI
    cargo llvm-cov --fail-under-lines 80  # Fail if below threshold
    

    カバレッジ目標

    コードの種類目標
    クリティカルなビジネスロジック100%
    パブリックAPI90%以上
    汎用コード80%以上
    生成済み / FFIバインディング除外

    テストコマンド

    cargo test                        # Run all tests
    cargo test -- --nocapture         # Show println output
    cargo test test_name              # Run tests matching pattern
    cargo test --lib                  # Unit tests only
    cargo test --test api_test        # Integration tests only
    cargo test --doc                  # Doc tests only
    cargo test --no-fail-fast         # Don't stop on first failure
    cargo test -- --ignored           # Run ignored tests
    

    ベストプラクティス

    すべきこと:

    • まずテストを書く (TDD)
    • 単体テストには #[cfg(test)] モジュールを使用する
    • 実装ではなく動作をテストする
    • シナリオを説明する記述的なテスト名を使用する
    • より良いエラーメッセージのために assert! より assert_eq! を優先する
    • クリーンなエラー出力のために Result を返すテストでは ? を使用する
    • テストを独立させる——共有の可変状態なし

    すべきでないこと:

    • Result::is_err() をテストできる場合に #[should_panic] を使用する
    • すべてをモックする——可能なら統合テストを優先する
    • フレーキーなテストを無視する——修正または分離する
    • テストで sleep() を使用する——チャンネル、バリア、または tokio::time::pause() を使用する
    • エラーパスのテストをスキップする

    CI統合

    # GitHub Actions
    test:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
        - uses: dtolnay/rust-toolchain@stable
          with:
            components: clippy, rustfmt
    
        - name: Check formatting
          run: cargo fmt --check
    
        - name: Clippy
          run: cargo clippy -- -D warnings
    
        - name: Run tests
          run: cargo test
    
        - uses: taiki-e/install-action@cargo-llvm-cov
    
        - name: Coverage
          run: cargo llvm-cov --fail-under-lines 80
    

    覚えておくこと:テストはドキュメントである。コードをどのように使うべきかを示している。明確に書き、最新の状態を保つこと。

    Alternatives

    Compare before choosing