rust-testing
Rust测试模式,包括单元测试、集成测试、异步测试、基于属性的测试、模拟和覆盖率。遵循TDD方法学。
DeepseekModel
Curated skill
Quality Excellent · 90
v1.0.0
Get
https://deepseekmodel.com/api/download.php?id=affaan-m-ecc-docs-zh-cn-skills-rust-testing-skill-md&format=skill
Download .skill
Standard format with system_prompt and model_config, ready for any agent framework
The actual content of the system_prompt field in the .skill file.
name rust-testing description Rust测试模式,包括单元测试、集成测试、异步测试、基于属性的测试、模拟和覆盖率。遵循TDD方法学。 origin ECC 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 错误与 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());
Keywords that activate this skill. Click one to copy it.
This skill does not provide trigger words.
The downloaded .skill package contains the following fields.
| Field | Description |
|---|---|
| format | Format tag (skill/v1) |
| skill_id | Unique skill ID |
| name | Skill name |
| version | Version |
| description | Description |
| category | Categories (array) |
| trigger_words | Trigger words |
| tags | Tags |
| source | Source |
| source_url | Source URL (this page) |
| exported_at | Exported at (set per download) |
| system_prompt | System prompt body |
| model_config | Model config: provider / model / temperature / max_tokens / top_p |
| examples | Examples |
| install_guide | Import guide for Coze / Dify / Claude / custom frameworks |
The same skill can be exported in different platform formats.