Source profileQuality 78/100Review permissions

affaan-m/ECC/docs/zh-CN/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/zh-CN/skills/rust-testing"
    Safe inspection promptEditorial

    Inspect the Agent Skill "rust-testing" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/zh-CN/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. 重构 — 改进代码同时保持测试通过 7. 检查覆盖率 — 使用 cargo-llvm-cov,目标 80% 以上

      识别目标代码 — 找到要测试的函数、特征或模块编写测试 — 在 [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/zh-CN/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
    

    错误与 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
    

    记住:测试就是文档。它们展示了你的代码应如何使用。清晰编写并保持更新。

    Alternatives

    Compare before choosing