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/zh-CN/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/zh-CN/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. 重构 — 改进代码同时保持测试通过 7. 检查覆盖率 — 使用 cargo-llvm-cov,目标 80% 以上

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/zh-CN/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 2 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

错误与 Panic 测试

测试 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
}

测试 Panic

#[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_eq! 而非 assert!
  • 在返回 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/zh-CN/skills/rust-testing/SKILL.md
Commit SHA
4e973d3eaf92
Repository license
MIT
Data collected