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テストパターン。
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
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
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.
npx skills add https://github.com/affaan-m/ECC --skill "docs/ja-JP/skills/rust-testing"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
- 01
使用場面
新しいRustの関数、メソッド、またはトレイトを書く場合 既存のコードにテストカバレッジを追加する場合 パフォーマンスクリティカルなコードのベンチマークを作成する場合 入力検証にプロパティベーステストを実装する場合 RustプロジェクトでTDDワークフローに従う場合
新しいRustの関数、メソッド、またはトレイトを書く場合既存のコードにテストカバレッジを追加する場合パフォーマンスクリティカルなコードのベンチマークを作成する場合 - 02
動作原理
1. ターゲットコードを特定する — テストする関数、トレイト、またはモジュールを見つける 2. テストを書く — [cfg(test)] モジュール内で [test] を使用、rstest でパラメータ化テスト、または proptest でプロパティベーステスト 3. 依存関係をモックする — mockall を使用してテスト対象のユニットを分離する 4. テストを実行する (RED) — テストが期待通りに失敗することを確認する 5. 実装する (GREEN) — テストを通過するための最小限のコードを書く 6. リファクタリングする — テストを…
ターゲットコードを特定する — テストする関数、トレイト、またはモジュールを見つけるテストを書く — [cfg(test)] モジュール内で [test] を使用、rstest でパラメータ化テスト、または proptest でプロパティベーステスト依存関係をモックする — mockall を使用してテスト対象のユニットを分離する - 03
RustのTDDワークフロー
Review the “RustのTDDワークフロー” section in the pinned source before continuing.
Review and apply the “RustのTDDワークフロー” source section. - 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. - 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
The documentation asks the agent to run terminal commands or scripts.
cargo llvm-cov # SummaryRuns scripts
The documentation asks the agent to run terminal commands or scripts.
cargo llvm-cov --html # HTML reportEvidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 78/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 234,327 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated 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ワークフローに従う場合
動作原理
- ターゲットコードを特定する — テストする関数、トレイト、またはモジュールを見つける
- テストを書く —
#[cfg(test)]モジュール内で#[test]を使用、rstest でパラメータ化テスト、または proptest でプロパティベーステスト - 依存関係をモックする — mockall を使用してテスト対象のユニットを分離する
- テストを実行する (RED) — テストが期待通りに失敗することを確認する
- 実装する (GREEN) — テストを通過するための最小限のコードを書く
- リファクタリングする — テストを通過したまま、コードを改善する
- カバレッジを確認する — 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% |
| パブリックAPI | 90%以上 |
| 汎用コード | 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
affaan-m/ECC
rust-testing
Rust testing patterns including unit tests, integration tests, async testing, property-based testing, mocking, and coverage. Follows TDD methodology.
affaan-m/ECC
rust-testing
Patrones de pruebas en Rust incluyendo pruebas unitarias, de integración, async, basadas en propiedades, mocking y cobertura. Sigue la metodología TDD.
affaan-m/ECC
rust-testing
Rust testing patterns including unit tests, integration tests, async testing, property-based testing, mocking, and coverage. Follows TDD methodology.
affaan-m/ECC
rust-testing
Use it for testing tasks; the detail page covers purpose, installation, and practical steps.