测试、文档与工程质量 intermediate 30 分钟 更新 2026-06-15

测试

用单元测试和集成测试保证代码正确。

测试

你会学到什么

  • #[test] 标注测试函数,cargo test 自动运行。
  • 单元测试放在被测模块内,可访问私有项;集成测试放在 tests/ 目录,只用公开 API。
  • assert!assert_eq!#[should_panic] 表达期望。

最小示例

#[cfg(test)]
mod tests {
    #[test]
    fn adds() {
        assert_eq!(2 + 3, 5);
    }
}

运行代码

cd examples
cargo run -p rt_30_testing
cargo test -p rt_30_testing

代码讲解

单元测试惯例上放在同一文件的 #[cfg(test)] mod tests 里,#[cfg(test)] 保证测试代码不会进入正式构建:

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn detects_even() {
        assert!(is_even(4));
    }
}

集成测试放在 crate 根的 tests/ 目录,每个文件是一个独立 crate,只能访问 pub API——这正好验证你的公开接口好不好用:

// tests/integration.rs
use rt_30_testing::add;
#[test]
fn adds_across_crate_boundary() {
    assert_eq!(add(10, 20), 30);
}

#[should_panic(expected = "...")] 断言某段代码会 panic 且信息匹配。

常见错误

忘记 use super::*;,测试里访问不到被测函数:

#[cfg(test)]
mod tests {
    #[test]
    fn adds() { assert_eq!(add(2, 3), 5); } // ❌ add 不在作用域
}

mod tests 顶部加 use super::*;

练习

  • add 写一个测试覆盖负数相加。
  • tests/ 里新增一个文件,测试 is_even 的边界值。

小结

cargo test 一条命令跑遍单元测试、集成测试和文档测试。单元测试查内部,集成测试查公开 API。

下一步

好的库还要有好文档。下一章学习文档注释与文档测试。

完整示例代码

下面是 examples/30_testing/ 的完整源码。无需 clone 仓库,直接在页面上阅读、复制、对照运行。

examples/30_testing/src/main.rs
use rt_30_testing::{add, is_even};

fn main() {
    println!("2 + 3 = {}", add(2, 3));
    println!("is_even(4) = {}", is_even(4));
}
examples/30_testing/src/lib.rs
//! 演示单元测试与集成测试。

/// 两数相加。
pub fn add(left: i64, right: i64) -> i64 {
    left + right
}

/// 判断是否为偶数。
pub fn is_even(value: i64) -> bool {
    value % 2 == 0
}

// 单元测试:和被测代码放在同一文件,可访问私有项。
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adds() {
        assert_eq!(add(2, 3), 5);
    }

    #[test]
    fn detects_even() {
        assert!(is_even(4));
        assert!(!is_even(5));
    }

    #[test]
    #[should_panic(expected = "boom")]
    fn can_assert_panic() {
        panic!("boom");
    }
}
examples/30_testing/tests/integration.rs
//! 集成测试:像外部用户一样,只能访问公开 API。

use rt_30_testing::{add, is_even};

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

#[test]
fn checks_even_across_crate_boundary() {
    assert!(is_even(0));
    assert!(!is_even(-3));
}
examples/30_testing/Cargo.toml
[package]
name = "rt_30_testing"
version.workspace = true
edition.workspace = true
publish.workspace = true