affaan-m/ECC

rust-testing

単体テスト、統合テスト、非同期テスト、プロパティベーステスト、モック、カバレッジを含むRustテストパターン。TDD方法論に従う。

78CollectingRuns scripts
See how to use itView GitHub source
npx skills add https://github.com/affaan-m/ECC --skill "docs/ja-JP/skills/rust-testing"
Automated source guide

Source checked Jul 28, 2026·Refresh due Oct 26, 2026

Reorganized from the pinned upstream SKILL.md

Turn rust-testing's source instructions into a guide you can follow

According to the pinned SKILL.md from affaan-m/ECC: TDD方法論に従って信頼性が高く保守しやすいテストを書くための包括的なRustテストパターン。

npx skills add https://github.com/affaan-m/ECC --skill "docs/ja-JP/skills/rust-testing"
Check the pinned source

Best fit

  • 単体テスト、統合テスト、非同期テスト、プロパティベーステスト、モック、カバレッジを含むRustテストパターン。TDD方法論に従う。

Bring this context

  • A concrete task that matches the documented purpose of rust-testing.
  • The files, examples, or context the task depends on.
  • Your constraints, target environment, and definition of done.

Expected outputs

  • A result that follows the pinned rust-testing instructions.
  • A concise record of assumptions, inputs used, and unresolved questions.
  • A final check against the source workflow and relevant permission signals.

Key source sections

Read rust-testing through these 5 source sections

Sections are extracted automatically from the pinned SKILL.md and link back to the source.

01

使用場面

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

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

動作原理

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

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

RustのTDDワークフロー

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

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

RED-GREEN-REFACTOR サイクル

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

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

Rustでの段階的TDD

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

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

SkillSignal prompt templates

Provide the task, context, and acceptance criteria

These prompts were written by SkillSignal from the source structure; they are not upstream text.

Task-start prompt

Confirm source fit, inputs, and outputs before acting.

Use rust-testing to help me with: [specific task]. Context: [files, data, or background]. Constraints: [environment, scope, and prohibited actions]. Before acting, check the pinned SKILL.md and explain which sections apply, what inputs are still missing, and what you will deliver.

Source-guided execution

Make the Agent explicitly follow the key extracted sections.

Apply the pinned rust-testing source to [task]. Pay particular attention to these source sections: “使用場面”, “動作原理”, “RustのTDDワークフロー”, “RED-GREEN-REFACTOR サイクル”, “Rustでの段階的TDD”. Preserve the important decision at each step. Mark facts not covered by the source as “needs confirmation” instead of inventing them. Then verify the result against my acceptance criteria: [criteria].

Result-review prompt

Check omissions, permissions, and source drift before delivery.

Review the current rust-testing result: (1) does it satisfy the original task; (2) were any applicable steps or limits in the pinned SKILL.md missed; (3) did it perform any unauthorized file, command, network, or data action; and (4) which conclusions remain unverified? List issues first, then fix only what the source or user authorization supports.

Output checklist

Verify each item before delivery

The task matches the purpose documented in the SKILL.md.

The source section “使用場面” has been checked.

The source section “動作原理” has been checked.

The source section “RustのTDDワークフロー” has been checked.

The source section “RED-GREEN-REFACTOR サイクル” has been checked.

Inputs, constraints, and acceptance criteria are explicit.

Unverified facts, compatibility, and outcome claims are clearly marked.

Any file, command, network, or data action has been reviewed.

Choose a different workflow

When another Skill is the better fit

FAQ

What does rust-testing do?

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

How do I start using rust-testing?

The catalog detected this source-specific install command: npx skills add https://github.com/affaan-m/ECC --skill "docs/ja-JP/skills/rust-testing". Inspect the command and pinned source before running it.

Which Agent platforms does it declare?

No dedicated Agent platform is declared in the pinned source record.

Repository stars
234,327
Repository forks
35,711
Quality
78/100
Source repository last pushed

Quality breakdown

Based on traceable docs and repository signals; stars are not treated as quality.

78/100
Documentation28/30
Specificity15/25
Maintenance20/20
Trust signals15/25
View original Skill.mdThis page is parsed directly from the repository SKILL.md without editorial rewriting. Collected: Jul 28, 2026 · about 1 min

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

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

Source repo
affaan-m/ECC
Skill path
docs/ja-JP/skills/rust-testing/SKILL.md
Commit SHA
4e973d3eaf92
Repository license
MIT
Data collected