[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DAJ3JME7QYQR3C6XMSD0VS":3,"skill-same:01M6DAJ3JME7QYQR3C6XMSD0VS":75},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":14,"repositoryHtmlUrl":46,"repositoryHomepageUrl":6,"ownerHtmlUrl":47,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":48,"install":49,"resources":62},"01M6DAJ3JME7QYQR3C6XMSD0VS","rust-testing",null,"Rust testing patterns including unit tests, integration tests, async testing, property-based testing, mocking, and coverage. Follows TDD methodology.","Prekzursil\u002Fagent-skills-toolchain","Prekzursil","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F54636077?v=4","01M6DACZ1PCQHD99SCR1YXG28B","Andrei Mihai Visalon","01M6DACZ670WZF535V3WYYSS4Z",0,"MIT","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Flibrary\u002Fskills\u002Frust-testing\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36],"testing","rust","rust testing","tdd","integration tests","unit tests","integration","methodology","testing patterns","coverage","async","patterns","tests","2026-11-10 10:12:30",false,"---\nname: rust-testing\ndescription: Rust testing patterns including unit tests, integration tests, async testing, property-based testing, mocking, and coverage. Follows TDD methodology.\norigin: ECC\n---\n\n# Rust Testing Patterns\n\nComprehensive Rust testing patterns for writing reliable, maintainable tests following TDD methodology.\n\n## When to Use\n\n- Writing new Rust functions, methods, or traits\n- Adding test coverage to existing code\n- Creating benchmarks for performance-critical code\n- Implementing property-based tests for input validation\n- Following TDD workflow in Rust projects\n\n## How It Works\n\n1. **Identify target code** — Find the function, trait, or module to test\n2. **Write a test** — Use `#[test]` in a `#[cfg(test)]` module, rstest for parameterized tests, or proptest for property-based tests\n3. **Mock dependencies** — Use mockall to isolate the unit under test\n4. **Run tests (RED)** — Verify the test fails with the expected error\n5. **Implement (GREEN)** — Write minimal code to pass\n6. **Refactor** — Improve while keeping tests green\n7. **Check coverage** — Use cargo-llvm-cov, target 80%+\n\n## TDD Workflow for Rust\n\n### The RED-GREEN-REFACTOR Cycle\n\n```\nRED     → Write a failing test first\nGREEN   → Write minimal code to pass the test\nREFACTOR → Improve code while keeping tests green\nREPEAT  → Continue with next requirement\n```\n\n### Step-by-Step TDD in Rust\n\n```rust\n\u002F\u002F RED: Write test first, use todo!() as placeholder\npub fn add(a: i32, b: i32) -> i32 { todo!() }\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    #[test]\n    fn test_add() { assert_eq!(add(2, 3), 5); }\n}\n\u002F\u002F cargo test → panics at 'not yet implemented'\n```\n\n```rust\n\u002F\u002F GREEN: Replace todo!() with minimal implementation\npub fn add(a: i32, b: i32) -> i32 { a + b }\n\u002F\u002F cargo test → PASS, then REFACTOR while keeping tests green\n```\n\n## Unit Tests\n\n### Module-Level Test Organization\n\n```rust\n\u002F\u002F src\u002Fuser.rs\npub struct User {\n    pub name: String,\n    pub email: String,\n}\n\nimpl User {\n    pub fn new(name: impl Into\u003CString>, email: impl Into\u003CString>) -> Result\u003CSelf, String> {\n        let email = email.into();\n        if !email.contains('@') {\n            return Err(format!(\"invalid email: {email}\"));\n        }\n        Ok(Self { name: name.into(), email })\n    }\n\n    pub fn display_name(&self) -> &str {\n        &self.name\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn creates_user_with_valid_email() {\n        let user = User::new(\"Alice\", \"alice@example.com\").unwrap();\n        assert_eq!(user.display_name(), \"Alice\");\n        assert_eq!(user.email, \"alice@example.com\");\n    }\n\n    #[test]\n    fn rejects_invalid_email() {\n        let result = User::new(\"Bob\", \"not-an-email\");\n        assert!(result.is_err());\n        assert!(result.unwrap_err().contains(\"invalid email\"));\n    }\n}\n```\n\n### Assertion Macros\n\n```rust\nassert_eq!(2 + 2, 4);                                    \u002F\u002F Equality\nassert_ne!(2 + 2, 5);                                    \u002F\u002F Inequality\nassert!(vec![1, 2, 3].contains(&2));                     \u002F\u002F Boolean\nassert_eq!(value, 42, \"expected 42 but got {value}\");    \u002F\u002F Custom message\nassert!((0.1_f64 + 0.2 - 0.3).abs() \u003C f64::EPSILON);   \u002F\u002F Float comparison\n```\n\n## Error and Panic Testing\n\n### Testing `Result` Returns\n\n```rust\n#[test]\nfn parse_returns_error_for_invalid_input() {\n    let result = parse_config(\"}{invalid\");\n    assert!(result.is_err());\n\n    \u002F\u002F Assert specific error variant\n    let err = result.unwrap_err();\n    assert!(matches!(err, ConfigError::ParseError(_)));\n}\n\n#[test]\nfn parse_succeeds_for_valid_input() -> Result\u003C(), Box\u003Cdyn std::error::Error>> {\n    let config = parse_config(r#\"{\"port\": 8080}\"#)?;\n    assert_eq!(config.port, 8080);\n    Ok(()) \u002F\u002F Test fails if any ? returns Err\n}\n```\n\n### Testing Panics\n\n```rust\n#[test]\n#[should_panic]\nfn panics_on_empty_input() {\n    process(&[]);\n}\n\n#[test]\n#[should_panic(expected = \"index out of bounds\")]\nfn panics_with_specific_message() {\n    let v: Vec\u003Ci32> = vec![];\n    let _ = v[0];\n}\n```\n\n## Integration Tests\n\n### File Structure\n\n```text\nmy_crate\u002F\n├── src\u002F\n│   └── lib.rs\n├── tests\u002F              # Integration tests\n│   ├── api_test.rs     # Each file is a separate test binary\n│   ├── db_test.rs\n│   └── common\u002F         # Shared test utilities\n│       └── mod.rs\n```\n\n### Writing Integration Tests\n\n```rust\n\u002F\u002F tests\u002Fapi_test.rs\nuse my_crate::{App, Config};\n\n#[test]\nfn full_request_lifecycle() {\n    let config = Config::test_default();\n    let app = App::new(config);\n\n    let response = app.handle_request(\"\u002Fhealth\");\n    assert_eq!(response.status, 200);\n    assert_eq!(response.body, \"OK\");\n}\n```\n\n## Async Tests\n\n### With Tokio\n\n```rust\n#[tokio::test]\nasync fn fetches_data_successfully() {\n    let client = TestClient::new().await;\n    let result = client.get(\"\u002Fdata\").await;\n    assert!(result.is_ok());\n    assert_eq!(result.unwrap().items.len(), 3);\n}\n\n#[tokio::test]\nasync fn handles_timeout() {\n    use std::time::Duration;\n    let result = tokio::time::timeout(\n        Duration::from_millis(100),\n        slow_operation(),\n    ).await;\n\n    assert!(result.is_err(), \"should have timed out\");\n}\n```\n\n## Test Organization Patterns\n\n### Parameterized Tests with `rstest`\n\n```rust\nuse rstest::{rstest, fixture};\n\n#[rstest]\n#[case(\"hello\", 5)]\n#[case(\"\", 0)]\n#[case(\"rust\", 4)]\nfn test_string_length(#[case] input: &str, #[case] expected: usize) {\n    assert_eq!(input.len(), expected);\n}\n\n\u002F\u002F Fixtures\n#[fixture]\nfn test_db() -> TestDb {\n    TestDb::new_in_memory()\n}\n\n#[rstest]\nfn test_insert(test_db: TestDb) {\n    test_db.insert(\"key\", \"value\");\n    assert_eq!(test_db.get(\"key\"), Some(\"value\".into()));\n}\n```\n\n### Test Helpers\n\n```rust\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    \u002F\u002F\u002F Creates a test user with sensible defaults.\n    fn make_user(name: &str) -> User {\n        User::new(name, &format!(\"{name}@test.com\")).unwrap()\n    }\n\n    #[test]\n    fn user_display() {\n        let user = make_user(\"alice\");\n        assert_eq!(user.display_name(), \"alice\");\n    }\n}\n```\n\n## Property-Based Testing with `proptest`\n\n### Basic Property Tests\n\n```rust\nuse proptest::prelude::*;\n\nproptest! {\n    #[test]\n    fn encode_decode_roundtrip(input in \".*\") {\n        let encoded = encode(&input);\n        let decoded = decode(&encoded).unwrap();\n        assert_eq!(input, decoded);\n    }\n\n    #[test]\n    fn sort_preserves_length(mut vec in prop::collection::vec(any::\u003Ci32>(), 0..100)) {\n        let original_len = vec.len();\n        vec.sort();\n        assert_eq!(vec.len(), original_len);\n    }\n\n    #[test]\n    fn sort_produces_ordered_output(mut vec in prop::collection::vec(any::\u003Ci32>(), 0..100)) {\n        vec.sort();\n        for window in vec.windows(2) {\n            assert!(window[0] \u003C= window[1]);\n        }\n    }\n}\n```\n\n### Custom Strategies\n\n```rust\nuse proptest::prelude::*;\n\nfn valid_email() -> impl Strategy\u003CValue = String> {\n    (\"[a-z]{1,10}\", \"[a-z]{1,5}\")\n        .prop_map(|(user, domain)| format!(\"{user}@{domain}.com\"))\n}\n\nproptest! {\n    #[test]\n    fn accepts_valid_emails(email in valid_email()) {\n        assert!(User::new(\"Test\", &email).is_ok());\n    }\n}\n```\n\n## Mocking with `mockall`\n\n### Trait-Based Mocking\n\n```rust\nuse mockall::{automock, predicate::eq};\n\n#[automock]\ntrait UserRepository {\n    fn find_by_id(&self, id: u64) -> Option\u003CUser>;\n    fn save(&self, user: &User) -> Result\u003C(), StorageError>;\n}\n\n#[test]\nfn service_returns_user_when_found() {\n    let mut mock = MockUserRepository::new();\n    mock.expect_find_by_id()\n        .with(eq(42))\n        .times(1)\n        .returning(|_| Some(User { id: 42, name: \"Alice\".into() }));\n\n    let service = UserService::new(Box::new(mock));\n    let user = service.get_user(42).unwrap();\n    assert_eq!(user.name, \"Alice\");\n}\n\n#[test]\nfn service_returns_none_when_not_found() {\n    let mut mock = MockUserRepository::new();\n    mock.expect_find_by_id()\n        .returning(|_| None);\n\n    let service = UserService::new(Box::new(mock));\n    assert!(service.get_user(99).is_none());\n}\n```\n\n## Doc Tests\n\n### Executable Documentation\n\n```rust\n\u002F\u002F\u002F Adds two numbers together.\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Examples\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F use my_crate::add;\n\u002F\u002F\u002F\n\u002F\u002F\u002F assert_eq!(add(2, 3), 5);\n\u002F\u002F\u002F assert_eq!(add(-1, 1), 0);\n\u002F\u002F\u002F ```\npub fn add(a: i32, b: i32) -> i32 {\n    a + b\n}\n\n\u002F\u002F\u002F Parses a config string.\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Errors\n\u002F\u002F\u002F\n\u002F\u002F\u002F Returns `Err` if the input is not valid TOML.\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```no_run\n\u002F\u002F\u002F use my_crate::parse_config;\n\u002F\u002F\u002F\n\u002F\u002F\u002F let config = parse_config(r#\"port = 8080\"#).unwrap();\n\u002F\u002F\u002F assert_eq!(config.port, 8080);\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```no_run\n\u002F\u002F\u002F use my_crate::parse_config;\n\u002F\u002F\u002F\n\u002F\u002F\u002F assert!(parse_config(\"}{invalid\").is_err());\n\u002F\u002F\u002F ```\npub fn parse_config(input: &str) -> Result\u003CConfig, ParseError> {\n    todo!()\n}\n```\n\n## Benchmarking with Criterion\n\n```toml\n# Cargo.toml\n[dev-dependencies]\ncriterion = { version = \"0.5\", features = [\"html_reports\"] }\n\n[[bench]]\nname = \"benchmark\"\nharness = false\n```\n\n```rust\n\u002F\u002F benches\u002Fbenchmark.rs\nuse criterion::{black_box, criterion_group, criterion_main, Criterion};\n\nfn fibonacci(n: u64) -> u64 {\n    match n {\n        0 | 1 => n,\n        _ => fibonacci(n - 1) + fibonacci(n - 2),\n    }\n}\n\nfn bench_fibonacci(c: &mut Criterion) {\n    c.bench_function(\"fib 20\", |b| b.iter(|| fibonacci(black_box(20))));\n}\n\ncriterion_group!(benches, bench_fibonacci);\ncriterion_main!(benches);\n```\n\n## Test Coverage\n\n### Running Coverage\n\n```bash\n# Install: cargo install cargo-llvm-cov (or use taiki-e\u002Finstall-action in CI)\ncargo llvm-cov                    # Summary\ncargo llvm-cov --html             # HTML report\ncargo llvm-cov --lcov > lcov.info # LCOV format for CI\ncargo llvm-cov --fail-under-lines 80  # Fail if below threshold\n```\n\n### Coverage Targets\n\n| Code Type | Target |\n|-----------|--------|\n| Critical business logic | 100% |\n| Public API | 90%+ |\n| General code | 80%+ |\n| Generated \u002F FFI bindings | Exclude |\n\n## Testing Commands\n\n```bash\ncargo test                        # Run all tests\ncargo test -- --nocapture         # Show println output\ncargo test test_name              # Run tests matching pattern\ncargo test --lib                  # Unit tests only\ncargo test --test api_test        # Integration tests only\ncargo test --doc                  # Doc tests only\ncargo test --no-fail-fast         # Don't stop on first failure\ncargo test -- --ignored           # Run ignored tests\n```\n\n## Best Practices\n\n**DO:**\n- Write tests FIRST (TDD)\n- Use `#[cfg(test)]` modules for unit tests\n- Test behavior, not implementation\n- Use descriptive test names that explain the scenario\n- Prefer `assert_eq!` over `assert!` for better error messages\n- Use `?` in tests that return `Result` for cleaner error output\n- Keep tests independent — no shared mutable state\n\n**DON'T:**\n- Use `#[should_panic]` when you can test `Result::is_err()` instead\n- Mock everything — prefer integration tests when feasible\n- Ignore flaky tests — fix or quarantine them\n- Use `sleep()` in tests — use channels, barriers, or `tokio::time::pause()`\n- Skip error path testing\n\n## CI Integration\n\n```yaml\n# GitHub Actions\ntest:\n  runs-on: ubuntu-latest\n  steps:\n    - uses: actions\u002Fcheckout@v4\n    - uses: dtolnay\u002Frust-toolchain@stable\n      with:\n        components: clippy, rustfmt\n\n    - name: Check formatting\n      run: cargo fmt --check\n\n    - name: Clippy\n      run: cargo clippy -- -D warnings\n\n    - name: Run tests\n      run: cargo test\n\n    - uses: taiki-e\u002Finstall-action@cargo-llvm-cov\n\n    - name: Coverage\n      run: cargo llvm-cov --fail-under-lines 80\n```\n\n**Remember**: Tests are documentation. They show how your code is meant to be used. Write them clearly and keep them up to date.\n","# Rust Testing Patterns\n\nComprehensive Rust testing patterns for writing reliable, maintainable tests following TDD methodology.\n\n## When to Use\n\n- Writing new Rust functions, methods, or traits\n- Adding test coverage to existing code\n- Creating benchmarks for performance-critical code\n- Implementing property-based tests for input validation\n- Following TDD workflow in Rust projects\n\n## How It Works\n\n1. **Identify target code** — Find the function, trait, or module to test\n2. **Write a test** — Use `#[test]` in a `#[cfg(test)]` module, rstest for parameterized tests, or proptest for property-based tests\n3. **Mock dependencies** — Use mockall to isolate the unit under test\n4. **Run tests (RED)** — Verify the test fails with the expected error\n5. **Implement (GREEN)** — Write minimal code to pass\n6. **Refactor** — Improve while keeping tests green\n7. **Check coverage** — Use cargo-llvm-cov, target 80%+\n\n## TDD Workflow for Rust\n\n### The RED-GREEN-REFACTOR Cycle\n\n```\nRED     → Write a failing test first\nGREEN   → Write minimal code to pass the test\nREFACTOR → Improve code while keeping tests green\nREPEAT  → Continue with next requirement\n```\n\n### Step-by-Step TDD in Rust\n\n```rust\n\u002F\u002F RED: Write test first, use todo!() as placeholder\npub fn add(a: i32, b: i32) -> i32 { todo!() }\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    #[test]\n    fn test_add() { assert_eq!(add(2, 3), 5); }\n}\n\u002F\u002F cargo test → panics at 'not yet implemented'\n```\n\n```rust\n\u002F\u002F GREEN: Replace todo!() with minimal implementation\npub fn add(a: i32, b: i32) -> i32 { a + b }\n\u002F\u002F cargo test → PASS, then REFACTOR while keeping tests green\n```\n\n## Unit Tests\n\n### Module-Level Test Organization\n\n```rust\n\u002F\u002F src\u002Fuser.rs\npub struct User {\n    pub name: String,\n    pub email: String,\n}\n\nimpl User {\n    pub fn new(name: impl Into\u003CString>, email: impl Into\u003CString>) -> Result\u003CSelf, String> {\n        let email = email.into();\n        if !email.contains('@') {\n            return Err(format!(\"invalid email: {email}\"));\n        }\n        Ok(Self { name: name.into(), email })\n    }\n\n    pub fn display_name(&self) -> &str {\n        &self.name\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn creates_user_with_valid_email() {\n        let user = User::new(\"Alice\", \"alice@example.com\").unwrap();\n        assert_eq!(user.display_name(), \"Alice\");\n        assert_eq!(user.email, \"alice@example.com\");\n    }\n\n    #[test]\n    fn rejects_invalid_email() {\n        let result = User::new(\"Bob\", \"not-an-email\");\n        assert!(result.is_err());\n        assert!(result.unwrap_err().contains(\"invalid email\"));\n    }\n}\n```\n\n### Assertion Macros\n\n```rust\nassert_eq!(2 + 2, 4);                                    \u002F\u002F Equality\nassert_ne!(2 + 2, 5);                                    \u002F\u002F Inequality\nassert!(vec![1, 2, 3].contains(&2));                     \u002F\u002F Boolean\nassert_eq!(value, 42, \"expected 42 but got {value}\");    \u002F\u002F Custom message\nassert!((0.1_f64 + 0.2 - 0.3).abs() \u003C f64::EPSILON);   \u002F\u002F Float comparison\n```\n\n## Error and Panic Testing\n\n### Testing `Result` Returns\n\n```rust\n#[test]\nfn parse_returns_error_for_invalid_input() {\n    let result = parse_config(\"}{invalid\");\n    assert!(result.is_err());\n\n    \u002F\u002F Assert specific error variant\n    let err = result.unwrap_err();\n    assert!(matches!(err, ConfigError::ParseError(_)));\n}\n\n#[test]\nfn parse_succeeds_for_valid_input() -> Result\u003C(), Box\u003Cdyn std::error::Error>> {\n    let config = parse_config(r#\"{\"port\": 8080}\"#)?;\n    assert_eq!(config.port, 8080);\n    Ok(()) \u002F\u002F Test fails if any ? returns Err\n}\n```\n\n### Testing Panics\n\n```rust\n#[test]\n#[should_panic]\nfn panics_on_empty_input() {\n    process(&[]);\n}\n\n#[test]\n#[should_panic(expected = \"index out of bounds\")]\nfn panics_with_specific_message() {\n    let v: Vec\u003Ci32> = vec![];\n    let _ = v[0];\n}\n```\n\n## Integration Tests\n\n### File Structure\n\n```text\nmy_crate\u002F\n├── src\u002F\n│   └── lib.rs\n├── tests\u002F              # Integration tests\n│   ├── api_test.rs     # Each file is a separate test binary\n│   ├── db_test.rs\n│   └── common\u002F         # Shared test utilities\n│       └── mod.rs\n```\n\n### Writing Integration Tests\n\n```rust\n\u002F\u002F tests\u002Fapi_test.rs\nuse my_crate::{App, Config};\n\n#[test]\nfn full_request_lifecycle() {\n    let config = Config::test_default();\n    let app = App::new(config);\n\n    let response = app.handle_request(\"\u002Fhealth\");\n    assert_eq!(response.status, 200);\n    assert_eq!(response.body, \"OK\");\n}\n```\n\n## Async Tests\n\n### With Tokio\n\n```rust\n#[tokio::test]\nasync fn fetches_data_successfully() {\n    let client = TestClient::new().await;\n    let result = client.get(\"\u002Fdata\").await;\n    assert!(result.is_ok());\n    assert_eq!(result.unwrap().items.len(), 3);\n}\n\n#[tokio::test]\nasync fn handles_timeout() {\n    use std::time::Duration;\n    let result = tokio::time::timeout(\n        Duration::from_millis(100),\n        slow_operation(),\n    ).await;\n\n    assert!(result.is_err(), \"should have timed out\");\n}\n```\n\n## Test Organization Patterns\n\n### Parameterized Tests with `rstest`\n\n```rust\nuse rstest::{rstest, fixture};\n\n#[rstest]\n#[case(\"hello\", 5)]\n#[case(\"\", 0)]\n#[case(\"rust\", 4)]\nfn test_string_length(#[case] input: &str, #[case] expected: usize) {\n    assert_eq!(input.len(), expected);\n}\n\n\u002F\u002F Fixtures\n#[fixture]\nfn test_db() -> TestDb {\n    TestDb::new_in_memory()\n}\n\n#[rstest]\nfn test_insert(test_db: TestDb) {\n    test_db.insert(\"key\", \"value\");\n    assert_eq!(test_db.get(\"key\"), Some(\"value\".into()));\n}\n```\n\n### Test Helpers\n\n```rust\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    \u002F\u002F\u002F Creates a test user with sensible defaults.\n    fn make_user(name: &str) -> User {\n        User::new(name, &format!(\"{name}@test.com\")).unwrap()\n    }\n\n    #[test]\n    fn user_display() {\n        let user = make_user(\"alice\");\n        assert_eq!(user.display_name(), \"alice\");\n    }\n}\n```\n\n## Property-Based Testing with `proptest`\n\n### Basic Property Tests\n\n```rust\nuse proptest::prelude::*;\n\nproptest! {\n    #[test]\n    fn encode_decode_roundtrip(input in \".*\") {\n        let encoded = encode(&input);\n        let decoded = decode(&encoded).unwrap();\n        assert_eq!(input, decoded);\n    }\n\n    #[test]\n    fn sort_preserves_length(mut vec in prop::collection::vec(any::\u003Ci32>(), 0..100)) {\n        let original_len = vec.len();\n        vec.sort();\n        assert_eq!(vec.len(), original_len);\n    }\n\n    #[test]\n    fn sort_produces_ordered_output(mut vec in prop::collection::vec(any::\u003Ci32>(), 0..100)) {\n        vec.sort();\n        for window in vec.windows(2) {\n            assert!(window[0] \u003C= window[1]);\n        }\n    }\n}\n```\n\n### Custom Strategies\n\n```rust\nuse proptest::prelude::*;\n\nfn valid_email() -> impl Strategy\u003CValue = String> {\n    (\"[a-z]{1,10}\", \"[a-z]{1,5}\")\n        .prop_map(|(user, domain)| format!(\"{user}@{domain}.com\"))\n}\n\nproptest! {\n    #[test]\n    fn accepts_valid_emails(email in valid_email()) {\n        assert!(User::new(\"Test\", &email).is_ok());\n    }\n}\n```\n\n## Mocking with `mockall`\n\n### Trait-Based Mocking\n\n```rust\nuse mockall::{automock, predicate::eq};\n\n#[automock]\ntrait UserRepository {\n    fn find_by_id(&self, id: u64) -> Option\u003CUser>;\n    fn save(&self, user: &User) -> Result\u003C(), StorageError>;\n}\n\n#[test]\nfn service_returns_user_when_found() {\n    let mut mock = MockUserRepository::new();\n    mock.expect_find_by_id()\n        .with(eq(42))\n        .times(1)\n        .returning(|_| Some(User { id: 42, name: \"Alice\".into() }));\n\n    let service = UserService::new(Box::new(mock));\n    let user = service.get_user(42).unwrap();\n    assert_eq!(user.name, \"Alice\");\n}\n\n#[test]\nfn service_returns_none_when_not_found() {\n    let mut mock = MockUserRepository::new();\n    mock.expect_find_by_id()\n        .returning(|_| None);\n\n    let service = UserService::new(Box::new(mock));\n    assert!(service.get_user(99).is_none());\n}\n```\n\n## Doc Tests\n\n### Executable Documentation\n\n```rust\n\u002F\u002F\u002F Adds two numbers together.\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Examples\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F use my_crate::add;\n\u002F\u002F\u002F\n\u002F\u002F\u002F assert_eq!(add(2, 3), 5);\n\u002F\u002F\u002F assert_eq!(add(-1, 1), 0);\n\u002F\u002F\u002F ```\npub fn add(a: i32, b: i32) -> i32 {\n    a + b\n}\n\n\u002F\u002F\u002F Parses a config string.\n\u002F\u002F\u002F\n\u002F\u002F\u002F # Errors\n\u002F\u002F\u002F\n\u002F\u002F\u002F Returns `Err` if the input is not valid TOML.\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```no_run\n\u002F\u002F\u002F use my_crate::parse_config;\n\u002F\u002F\u002F\n\u002F\u002F\u002F let config = parse_config(r#\"port = 8080\"#).unwrap();\n\u002F\u002F\u002F assert_eq!(config.port, 8080);\n\u002F\u002F\u002F ```\n\u002F\u002F\u002F\n\u002F\u002F\u002F ```no_run\n\u002F\u002F\u002F use my_crate::parse_config;\n\u002F\u002F\u002F\n\u002F\u002F\u002F assert!(parse_config(\"}{invalid\").is_err());\n\u002F\u002F\u002F ```\npub fn parse_config(input: &str) -> Result\u003CConfig, ParseError> {\n    todo!()\n}\n```\n\n## Benchmarking with Criterion\n\n```toml\n# Cargo.toml\n[dev-dependencies]\ncriterion = { version = \"0.5\", features = [\"html_reports\"] }\n\n[[bench]]\nname = \"benchmark\"\nharness = false\n```\n\n```rust\n\u002F\u002F benches\u002Fbenchmark.rs\nuse criterion::{black_box, criterion_group, criterion_main, Criterion};\n\nfn fibonacci(n: u64) -> u64 {\n    match n {\n        0 | 1 => n,\n        _ => fibonacci(n - 1) + fibonacci(n - 2),\n    }\n}\n\nfn bench_fibonacci(c: &mut Criterion) {\n    c.bench_function(\"fib 20\", |b| b.iter(|| fibonacci(black_box(20))));\n}\n\ncriterion_group!(benches, bench_fibonacci);\ncriterion_main!(benches);\n```\n\n## Test Coverage\n\n### Running Coverage\n\n```bash\n# Install: cargo install cargo-llvm-cov (or use taiki-e\u002Finstall-action in CI)\ncargo llvm-cov                    # Summary\ncargo llvm-cov --html             # HTML report\ncargo llvm-cov --lcov > lcov.info # LCOV format for CI\ncargo llvm-cov --fail-under-lines 80  # Fail if below threshold\n```\n\n### Coverage Targets\n\n| Code Type | Target |\n|-----------|--------|\n| Critical business logic | 100% |\n| Public API | 90%+ |\n| General code | 80%+ |\n| Generated \u002F FFI bindings | Exclude |\n\n## Testing Commands\n\n```bash\ncargo test                        # Run all tests\ncargo test -- --nocapture         # Show println output\ncargo test test_name              # Run tests matching pattern\ncargo test --lib                  # Unit tests only\ncargo test --test api_test        # Integration tests only\ncargo test --doc                  # Doc tests only\ncargo test --no-fail-fast         # Don't stop on first failure\ncargo test -- --ignored           # Run ignored tests\n```\n\n## Best Practices\n\n**DO:**\n- Write tests FIRST (TDD)\n- Use `#[cfg(test)]` modules for unit tests\n- Test behavior, not implementation\n- Use descriptive test names that explain the scenario\n- Prefer `assert_eq!` over `assert!` for better error messages\n- Use `?` in tests that return `Result` for cleaner error output\n- Keep tests independent — no shared mutable state\n\n**DON'T:**\n- Use `#[should_panic]` when you can test `Result::is_err()` instead\n- Mock everything — prefer integration tests when feasible\n- Ignore flaky tests — fix or quarantine them\n- Use `sleep()` in tests — use channels, barriers, or `tokio::time::pause()`\n- Skip error path testing\n\n## CI Integration\n\n```yaml\n# GitHub Actions\ntest:\n  runs-on: ubuntu-latest\n  steps:\n    - uses: actions\u002Fcheckout@v4\n    - uses: dtolnay\u002Frust-toolchain@stable\n      with:\n        components: clippy, rustfmt\n\n    - name: Check formatting\n      run: cargo fmt --check\n\n    - name: Clippy\n      run: cargo clippy -- -D warnings\n\n    - name: Run tests\n      run: cargo test\n\n    - uses: taiki-e\u002Finstall-action@cargo-llvm-cov\n\n    - name: Coverage\n      run: cargo llvm-cov --fail-under-lines 80\n```\n\n**Remember**: Tests are documentation. They show how your code is meant to be used. Write them clearly and keep them up to date.\n",{"name":5,"origin":42,"description":7},"ECC","main","a298c929b6cbd662e1fce54302ce520d5ceea1a5",".agents\u002Flibrary\u002Fskills\u002Frust-testing","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain","https:\u002F\u002Fgithub.com\u002FPrekzursil","2026-11-02 14:30:57",{"source":8,"skillName":5,"commands":50,"prompt":60,"zipAvailable":61},[51,54,57],{"tool":52,"command":53},"npx","npx skills add Prekzursil\u002Fagent-skills-toolchain --skill rust-testing",{"tool":55,"command":56},"bunx","bunx skills add Prekzursil\u002Fagent-skills-toolchain --skill rust-testing",{"tool":58,"command":59},"pnpm","pnpm dlx skills add Prekzursil\u002Fagent-skills-toolchain --skill rust-testing","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Flibrary\u002Fskills\u002Frust-testing\u002FSKILL.md",true,[63,69],{"path":64,"resourceType":65,"resourceTypeDesc":66,"sizeBytes":67,"sourceHtmlUrl":68},"checklist.md","REFERENCE","参考文档",4389,"https:\u002F\u002Fgithub.com\u002Ftonygomes-wq\u002Fevo\u002Fblob\u002Fmain\u002F.kiro\u002Fskills\u002Fgds-dev-story\u002Fchecklist.md",{"path":70,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":73,"sourceHtmlUrl":74},"customize.toml","OTHER","其他",1658,"https:\u002F\u002Fgithub.com\u002Ftonygomes-wq\u002Fevo\u002Fblob\u002Fmain\u002F.kiro\u002Fskills\u002Fgds-dev-story\u002Fcustomize.toml",[76,98,127,152,166,182],{"skillNo":77,"name":78,"nameZh":6,"description":79,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":80,"categories":81,"keywords":82,"publishedAt":37},"01M6DF7D7478R55P46REYH4QAC","clarify-requirements","Resolve genuine pre-execution ambiguity by scanning repo context, generating 1-6 grounded options, and asking 1-3 focused questions via AskUserQuestion. NOT for mid-task use (injection risk); NOT when the goal is already unambiguous; use to disambiguate vague prompts before any file edits.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fworkspace-hooks\u002Fskills\u002Fclarify-requirements\u002FSKILL.md",[],[83,84,85,86,87,88,89,90,91,92,93,94,95,96,97],"clarify requirements","clarify","requirements","risk","options","task","prompts","goal","scanning","use","context","repo","injection","execution","file",{"skillNo":99,"name":100,"nameZh":6,"description":101,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":107,"publishedAt":37},"01M6DF79CA4BHJS66J8EZDBPWJ","openmm-md","Run FREE, open-source, Windows-native GPU molecular dynamics via the openmm-md MCP (OpenMM + CUDA on the local NVIDIA GPU). Use for protein \u002F protein-ligand MD when you want a free stack or are on Windows without a Schrodinger license. Covers the full workflow (prepare, solvate, minimize, equilibrate, production, analyze, mmgbsa) AND an honest decision guide vs Schrodinger Desmond, including how to run Desmond legitimately on Linux or a cloud GPU with a real license. This is a deliberately LESSER free alternative and does NOT replace the schrodinger MCP tools. Do NOT use it to pick a free tool when a validated FEP+, WaterMap, IFD-MD, or Prime MM-GBSA result is actually required.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fmcp\u002Fopenmm-md-mcp\u002Fskill\u002Fopenmm-md\u002FSKILL.md",[104],{"categoryNo":105,"categoryName":106,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[108,109,110,111,112,113,114,115,116,117,118,119,120,121,92,122,123,124,125,126],"openmm md","how","decision","pick","linux","analyze","tool","stack","open","NVIDIA","gpu","license","workflow","windows","free","molecular dynamics","local","do","MCP",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":137,"publishedAt":37},"01M6DF76C1MG85VEWP8VZCBNMN","visual-review","Take screenshots of web pages and UI using Playwright for visual review and iteration","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fvisual-review\u002FSKILL.md",[133,136],{"categoryNo":134,"categoryName":135,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[138,139,140,141,142,143,144,145,146,147,109,148,149,150,151],"review","visual","visual review","pages","web","false","iteration","take","check","slides","ui","screenshot","playwright","screenshots",{"skillNo":153,"name":154,"nameZh":6,"description":155,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":156,"categories":157,"keywords":158,"publishedAt":37},"01M6DF73AQVCBZKEN0YXEX9FXP","status","Diagnostic status report — shows metaswarm installation state, project setup, and potential issues","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstatus\u002FSKILL.md",[],[154,159,160,161,162,163,164,165],"setup","report","project","installation","project setup","issues","state",{"skillNo":167,"name":168,"nameZh":6,"description":169,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":170,"categories":171,"keywords":173,"publishedAt":37},"01M6DF6X6C2XB0P6GX76P7TTYV","start","Use when starting work on any task, when the user mentions metaswarm, or when the user wants to begin tracked development work","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstart\u002FSKILL.md",[172],{"categoryNo":105,"categoryName":106,"taxonomyType":21,"taxonomyTypeDesc":22},[168,174,88,175,176,177,92,178,179,180,181],"start issue","issue","ready","work on issue","user","work","work on","development",{"skillNo":183,"name":159,"nameZh":6,"description":184,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":185,"categories":186,"keywords":187,"publishedAt":37},"01M6DF6QMJ9AJKFKHHKRT005PQ","Interactive project setup — detects your project, configures metaswarm, writes project-local files","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fsetup\u002FSKILL.md",[],[159,161,124,188,163,189],"files","interactive"]