Rust进阶[part9]_test测试

Rust进阶[part9]_test测试

Unit Test(单元测试)

单元测试用于测试代码中的最小功能单元(如函数、方法),通常与被测试代码放在同一模块中,方便测试私有接口。

基本示例

创建src/lib.rs,编写公共函数及对应的单元测试:

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

pub fn multiply(a: i32, b: i32) -> i32 {
    a * b
}

// 测试模块(通常命名为test,使用#[cfg(test)]标记仅在测试模式下编译)
#[cfg(test)]
mod tests {
    // 引入父模块的函数(因测试模块是子模块,需显式引入)
    use super::*;

    // 测试add函数:正常情况
    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5); // 断言相等
        assert_ne!(add(10, 20), 25); // 断言不相等
    }

    // 测试multiply函数:边界情况
    #[test]
    fn test_multiply() {
        assert_eq!(multiply(0, 5), 0); // 乘以0
        assert_eq!(multiply(-3, 4), -12); // 负数相乘
    }

    // 标记应该失败的测试(用于验证错误处理)
    #[test]
    #[should_panic(expected = "division by zero")] // 预期panic信息
    fn test_divide_by_zero() {
        let _ = 1 / 0;
    }

    // 忽略测试(暂时不执行)
    #[test]
    #[ignore = "尚未实现完整逻辑"]
    fn test_complex_calculation() {
        // 待实现的测试逻辑
    }
}

代码解释:

  • #[cfg(test)]:标记测试模块,仅在运行cargo test时才编译,避免影响生产代码
  • #[test]:标记测试函数,cargo会自动识别并执行
  • 断言宏:assert_eq!(a, b)(检查a等于b)、assert_ne!(a, b)(检查a不等于b)、assert!(condition)(检查条件为真)
  • #[should_panic]:标记测试预期会panic,若未panic则测试失败;可通过expected指定预期的panic信息
  • #[ignore]:标记测试暂时忽略,执行cargo test -- --ignored可单独运行忽略的测试

Integration Test(集成测试)

集成测试用于测试多个模块或组件协同工作的情况,通常放在独立的tests目录中,只能访问被测试代码的公共接口。

目录结构

src同级创建tests文件夹(cargo会自动识别为集成测试目录):

project/
├── src/
│   └── lib.rs
├── tests/
│   ├── basic_test.rs  // 集成测试文件1
│   └── advanced_test.rs  // 集成测试文件2
└── Cargo.toml

示例代码

tests/basic_test.rs

// 引入被测试的库(需在Cargo.toml中配置库名称)
use my_library::{add, multiply};

#[test]
fn test_integration_add() {
    assert_eq!(add(10, 20), 30);
}

#[test]
fn test_integration_multiply() {
    assert_eq!(multiply(3, 7), 21);
}

配置说明

需在Cargo.toml中配置库信息,使集成测试能正确引入:

[lib]
name = "my_library"  # 库名称(集成测试中use的名称)
path = "src/lib.rs"  # 库文件路径

测试命令行

命令 作用
cargo test 运行所有测试(单元测试+集成测试+文档测试)
cargo test test_add 运行名称包含test_add的测试(模糊匹配)
cargo test tests::test_multiply 运行指定模块下的测试
cargo test --test basic_test 仅运行tests/basic_test.rs中的集成测试
cargo test -- --nocapture 运行测试并显示测试中的打印输出(默认捕获输出)
cargo test -- --ignored 仅运行被#[ignore]标记的测试
cargo test --release 在release模式下运行测试(性能更接近生产环境)

Doc Tests(文档测试)

文档测试是嵌入在代码文档中的测试,既能作为示例文档,又能被cargo执行,确保文档与代码行为一致。

基本用法

在函数的文档注释(///)中通过代码块编写测试:

/// 两数相加
/// 
/// # Examples
/// 
/// ```
/// use my_library::add;
/// 
/// assert_eq!(add(2, 3), 5);
/// assert_eq!(add(100, 200), 300);
/// ```
pub fn add(left: usize, right: usize) -> usize {
    left + right
}

执行文档测试

cargo test --doc  # 仅运行文档测试

注意事项

  • 文档测试代码块必须是完整可执行的(需包含use引入)
  • 可使用/// # fn main() { ... }包裹复杂示例(适用于需要主函数的场景)
  • 若示例代码预期会 panic,可在代码块前加should_panic标记:
    /// # Examples
    /// 
    /// ```should_panic
    /// use my_library::divide;
    /// divide(1, 0);  // 预期panic
    /// ```
    pub fn divide(a: i32, b: i32) -> i32 {
        if b == 0 {
            panic!("division by zero");
        }
        a / b
    }

Examples(示例代码)

examples目录用于存放可运行的示例代码,这些示例不仅是文档,还能通过cargo test验证其可执行性。

目录结构

project/
├── src/
│   └── lib.rs
└── examples/
    ├── simple_usage.rs  # 简单示例
    └── advanced/  # 多文件示例
        ├── main.rs
        └── helper.rs

示例代码

examples/simple_usage.rs

use my_library::add;

fn main() {
    let result = add(5, 7);
    println!("5 + 7 = {}", result);
}

运行示例测试

cargo test --examples  # 运行所有示例(验证能否编译执行)
cargo run --example simple_usage  # 直接运行指定示例

标准工程目录规范详解

├── Cargo.lock  # 依赖版本锁定文件(自动生成,不要手动修改)  
├── Cargo.toml  # 项目配置文件(依赖、库信息等)  
├── src/  
│   ├── lib.rs  # 库代码入口(被单元测试和集成测试引用)  
│   ├── main.rs  # 主程序入口(若项目是可执行程序)  
│   └── bin/  # 多可执行文件目录(每个.rs文件对应一个可执行程序)  
│       ├── tool1.rs  
│       └── tool2.rs  
├── benches/  # 基准测试目录(性能测试)  
│   ├── performance.rs  
│   └── heavy_operation.rs  
│       # 基准测试示例:  
│       # #[bench]  
│       # fn bench_add(b: &mut Bencher) {  
│       #     b.iter(|| add(1, 2));  
│       # }  
├── examples/  # 示例代码目录(见上文Examples部分)  
└── tests/  # 集成测试目录(见上文Integration Test部分)  
  • src/bin:当项目需要多个可执行程序时使用,运行cargo run --bin tool1可执行对应程序
  • benches:基准测试使用#[bench]标记,通过cargo bench运行,用于评估代码性能
  • 所有测试相关目录(testsbenchesexamples)的代码都遵循"仅在测试/运行时编译"的原则,不影响生产环境代码体积

练习

创建并发布一个 Rust crate 到 crates.io,涵盖单元测试、集成测试、文档测试、examples 以及项目规范,甚至进阶的 GitHub Actions 自动发布。

首先,得理清楚整个流程的步骤,从初始化项目、编写代码、添加测试、配置文档,到发布和自动化。