Skip to content

测试策略

> 确保代码质量和功能正确性


测试类型

本项目包含三种测试:

1. 单元测试

测试单个函数和模块的功能。

位置: src/lib.rs 或各模块文件中的 #[cfg(test)] 模块

示例:

rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add_todo() {
        let mut todo = TodoList::new();
        todo.add("Learn Rust".to_string());
        assert_eq!(todo.len(), 1);
    }

    #[test]
    fn test_complete_todo() {
        let mut todo = TodoList::new();
        let id = todo.add("Write code".to_string());
        todo.complete(id).unwrap();
        assert!(todo.get(id).unwrap().completed);
    }
}
▶ Run

运行单元测试:

bash
cargo test

2. 集成测试

测试多个模块的协作和完整功能流程。

位置: tests/ 目录

示例: tests/integration_test.rs

rust
use todo_cli::{TodoList, Storage};

#[test]
fn test_full_workflow() {
    // 创建临时存储
    let temp_file = tempfile::NamedTempFile::new().unwrap();
    let storage = Storage::new(temp_file.path());
    
    // 创建待办列表
    let mut todo = TodoList::new();
    todo.add("Task 1".to_string());
    todo.add("Task 2".to_string());
    
    // 保存到存储
    storage.save(&todo).unwrap();
    
    // 从存储加载
    let loaded = storage.load().unwrap();
    assert_eq!(loaded.len(), 2);
}
▶ Run

运行集成测试:

bash
cargo test --test integration_test

3. 文档测试

测试文档中的代码示例。

位置: 文档注释 /// 中的代码块

示例:

rust
/// 添加新的待办事项
/// 
/// # Examples
/// 
/// ```
/// use todo_cli::TodoList;
/// 
/// let mut todo = TodoList::new();
/// todo.add("Buy milk".to_string());
/// assert_eq!(todo.len(), 1);
/// ```
pub fn add(&mut self, text: String) -> usize {
    // ...
}
▶ Run

运行文档测试:

bash
cargo test --doc

测试策略

测试金字塔

        ┌─────────┐
        │  E2E    │  少量端到端测试
        │ Tests   │  (测试完整流程)
        ├─────────┤
        │Integration│  中量集成测试
        │  Tests   │  (测试模块协作)
        ├─────────┤
        │  Unit    │  大量单元测试
        │  Tests   │  (测试单个函数)
        └─────────┘

测试覆盖范围

必须测试:

  • ✅ 核心业务逻辑(添加、删除、完成待办)
  • ✅ 错误处理(无效输入、文件操作失败)
  • ✅ 边界条件(空列表、重复 ID)
  • ✅ 数据持久化(保存、加载)

可选测试:

  • ⚠️ CLI 参数解析(集成测试覆盖)
  • ⚠️ 彩色输出(人工测试即可)

测试工具

测试框架

  • 内置框架: #[test] 属性
  • 断言宏: assert!, assert_eq!, assert_ne!
  • 测试属性: #[should_panic], #[ignore]

测试辅助库

toml
[dev-dependencies]
tempfile = "3.3"        # 临时文件
assert_cmd = "2.0"      # CLI 测试
predicates = "3.0"      # 断言谓词

示例: 使用 assert_cmd 测试 CLI

rust
use assert_cmd::Command;

#[test]
fn test_cli_add() {
    let mut cmd = Command::cargo_bin("todo-cli").unwrap();
    cmd.arg("add")
       .arg("Buy groceries")
       .assert()
       .success()
       .stdout(predicates::str::contains("Added"));
}
▶ Run

测试场景

场景 1:添加待办事项

rust
#[test]
fn test_add_todo_basic() {
    let mut list = TodoList::new();
    let id = list.add("Learn Rust".to_string());
    
    assert_eq!(id, 0);
    assert_eq!(list.len(), 1);
    assert_eq!(list.get(id).unwrap().text, "Learn Rust");
}

#[test]
fn test_add_multiple_todos() {
    let mut list = TodoList::new();
    list.add("Task 1".to_string());
    list.add("Task 2".to_string());
    
    assert_eq!(list.len(), 2);
}
▶ Run

场景 2:完成待办事项

rust
#[test]
fn test_complete_todo() {
    let mut list = TodoList::new();
    let id = list.add("Write tests".to_string());
    
    assert!(!list.get(id).unwrap().completed);
    
    list.complete(id).unwrap();
    assert!(list.get(id).unwrap().completed);
}

#[test]
fn test_complete_nonexistent_todo() {
    let mut list = TodoList::new();
    let result = list.complete(999);
    
    assert!(result.is_err());
}
▶ Run

场景 3:持久化存储

rust
#[test]
fn test_save_and_load() {
    let temp_file = tempfile::NamedTempFile::new().unwrap();
    let storage = Storage::new(temp_file.path());
    
    let mut list = TodoList::new();
    list.add("Task 1".to_string());
    list.add("Task 2".to_string());
    
    // 保存
    storage.save(&list).unwrap();
    
    // 加载
    let loaded = storage.load().unwrap();
    assert_eq!(loaded.len(), 2);
    assert_eq!(loaded.get(0).unwrap().text, "Task 1");
}
▶ Run

测试命令

运行所有测试

bash
cargo test

运行特定测试

bash
# 运行单个测试
cargo test test_add_todo

# 运行匹配模式的测试
cargo test add

# 运行特定模块的测试
cargo test tests::todo_list

显示测试输出

bash
# 显示 println! 输出
cargo test -- --nocapture

# 显示测试详细输出
cargo test -- --show-output

并行测试

bash
# 单线程运行
cargo test -- --test-threads=1

# 指定线程数
cargo test -- --test-threads=4

测试覆盖率

bash
# 安装 tarpaulin
cargo install cargo-tarpaulin

# 生成覆盖率报告
cargo tarpaulin --out Html

测试最佳实践

1. 测试命名

rust
// ✅ 好的命名
#[test]
fn test_add_todo_increases_length() { }

#[test]
fn test_complete_nonexistent_todo_returns_error() { }

// ❌ 差的命名
#[test]
fn test_add() { }

#[test]
fn test_error() { }
▶ Run

2. 测试结构

rust
#[test]
fn test_complete_todo() {
    // Arrange(准备)
    let mut list = TodoList::new();
    let id = list.add("Task".to_string());
    
    // Act(执行)
    let result = list.complete(id);
    
    // Assert(断言)
    assert!(result.is_ok());
    assert!(list.get(id).unwrap().completed);
}
▶ Run

3. 测试隔离

rust
#[test]
fn test_with_temporary_file() {
    // 使用临时文件,测试结束后自动删除
    let temp_file = tempfile::NamedTempFile::new().unwrap();
    let storage = Storage::new(temp_file.path());
    
    // 测试代码...
}
▶ Run

4. 测试错误情况

rust
#[test]
fn test_invalid_id() {
    let list = TodoList::new();
    
    // 测试错误情况
    assert!(list.get(999).is_none());
    assert!(list.complete(999).is_err());
}

#[test]
#[should_panic(expected = "index out of bounds")]
fn test_panic_on_invalid_access() {
    let list = TodoList::new();
    list.get(0).unwrap(); // 应该 panic
}
▶ Run

测试检查清单

完成以下测试后,即可认为项目测试充分:

  • [ ] 单元测试

    • [ ] TodoList::new()
    • [ ] TodoList::add()
    • [ ] TodoList::complete()
    • [ ] TodoList::delete()
    • [ ] TodoList::list()
    • [ ] Storage::save()
    • [ ] Storage::load()
  • [ ] 集成测试

    • [ ] 完整工作流(添加→完成→保存→加载)
    • [ ] CLI 参数解析
    • [ ] 错误处理流程
  • [ ] 文档测试

    • [ ] 公共 API 文档示例
    • [ ] README 中的代码示例
  • [ ] 边界测试

    • [ ] 空列表操作
    • [ ] 无效 ID 处理
    • [ ] 文件不存在处理
    • [ ] 权限错误处理

持续集成

.github/workflows/ci.yml 中配置测试:

yaml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
      - name: Run tests
        run: cargo test --all-features
      - name: Run clippy
        run: cargo clippy -- -D warnings

相关资源


更新日志:

  • 2026-04-03: 创建测试策略文档