Cargo 测试 #
测试概述 #
Cargo 内置了强大的测试框架,支持单元测试、集成测试和文档测试。本节将详细介绍如何使用 Cargo 编写和运行测试。
测试类型 #
text
测试类型
├── 单元测试
│ ├── 与代码放在同一文件
│ ├── 使用 #[test] 属性标记
│ └── 可以测试私有函数
├── 集成测试
│ ├── 放在 tests/ 目录
│ ├── 测试公共 API
│ └── 可以使用外部依赖
└── 文档测试
├── 在文档注释中的代码示例
├── 自动编译和运行
└── 确保文档与代码同步
单元测试 #
基本语法 #
rust
// src/lib.rs 或 src/main.rs
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
fn another_test() {
assert!(true);
}
}
断言宏 #
assert! 宏 #
rust
#[test]
fn test_assert() {
let value = true;
assert!(value, "value should be true");
}
assert_eq! 和 assert_ne! 宏 #
rust
#[test]
fn test_assert_eq() {
let result = 2 + 2;
assert_eq!(result, 4, "2 + 2 should equal 4");
}
#[test]
fn test_assert_ne() {
let result = 2 + 2;
assert_ne!(result, 5, "2 + 2 should not equal 5");
}
自定义错误消息 #
rust
#[test]
fn test_with_message() {
let a = 1;
let b = 2;
assert_eq!(
a + b,
3,
"a ({}) + b ({}) should equal 3",
a,
b
);
}
测试 Result #
rust
#[test]
fn test_result() -> Result<(), String> {
let result = some_function()?;
assert!(result.is_ok());
Ok(())
}
fn some_function() -> Result<bool, String> {
Ok(true)
}
测试 panic #
rust
#[test]
#[should_panic]
fn test_panic() {
panic!("This should panic");
}
#[test]
#[should_panic(expected = "division by zero")]
fn test_panic_with_message() {
divide(1, 0);
}
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("division by zero");
}
a / b
}
忽略测试 #
rust
#[test]
#[ignore]
fn expensive_test() {
// 耗时较长的测试
}
测试私有函数 #
rust
// src/lib.rs
fn internal_function(x: i32) -> i32 {
x * 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_internal() {
assert_eq!(internal_function(5), 10);
}
}
集成测试 #
基本结构 #
text
my_project/
├── Cargo.toml
├── src/
│ └── lib.rs
└── tests/
├── integration_test.rs
└── common/
└── mod.rs
创建集成测试 #
rust
// tests/integration_test.rs
use my_lib;
#[test]
fn test_add() {
assert_eq!(my_lib::add(2, 3), 5);
}
#[test]
fn test_public_api() {
// 测试公共 API
}
共享模块 #
rust
// tests/common/mod.rs
pub fn setup() {
// 测试前设置
}
pub fn teardown() {
// 测试后清理
}
rust
// tests/integration_test.rs
mod common;
#[test]
fn test_with_setup() {
common::setup();
// 测试代码
}
多个测试文件 #
text
tests/
├── api_test.rs # API 测试
├── db_test.rs # 数据库测试
├── integration.rs # 集成测试
└── common/
└── mod.rs # 共享模块
文档测试 #
基本语法 #
rust
/// Adds two numbers.
///
/// # Examples
///
/// ```
/// use my_lib::add;
///
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
忽略文档测试 #
rust
/// ```ignore
/// use my_lib::some_function;
///
/// some_function(); // 这个测试会被忽略
/// ```
pub fn some_function() {}
编译失败测试 #
rust
/// ```compile_fail
/// use my_lib::must_fail;
///
/// must_fail(); // 这个应该编译失败
/// ```
pub fn must_fail() -> ! {
unimplemented!()
}
无运行测试 #
rust
/// ```no_run
/// use my_lib::server;
///
/// server::run(); // 编译但不运行
/// ```
pub fn run() {}
添加依赖 #
rust
/// ```rust
/// use my_lib::add;
/// use serde_json::json;
///
/// let result = add(1, 2);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
测试命令 #
基本命令 #
bash
# 运行所有测试
cargo test
# 运行特定测试
cargo test test_name
# 运行匹配模式的测试
cargo test test_prefix_
# 运行特定模块的测试
cargo test tests::module_name
测试类型选择 #
bash
# 只运行单元测试
cargo test --lib
# 只运行集成测试
cargo test --test integration_test
# 只运行文档测试
cargo test --doc
# 运行所有测试
cargo test --all-targets
并行控制 #
bash
# 并行运行(默认)
cargo test
# 指定线程数
cargo test -- --test-threads=4
# 串行运行
cargo test -- --test-threads=1
输出控制 #
bash
# 显示测试输出
cargo test -- --nocapture
# 显示成功测试的输出
cargo test -- --show-output
# 静默模式
cargo test -- --quiet
忽略测试 #
bash
# 运行被忽略的测试
cargo test -- --ignored
# 运行所有测试(包括被忽略的)
cargo test -- --include-ignored
发布模式测试 #
bash
# 发布模式测试
cargo test --release
工作区测试 #
bash
# 测试所有工作区成员
cargo test --workspace
# 测试特定包
cargo test -p my_lib
测试组织 #
测试目录结构 #
text
my_project/
├── Cargo.toml
├── src/
│ ├── lib.rs
│ ├── main.rs
│ └── module/
│ └── mod.rs
└── tests/
├── integration_test.rs
├── api/
│ └── user_test.rs
└── common/
└── mod.rs
测试模块组织 #
rust
// src/lib.rs
#[cfg(test)]
mod tests {
mod unit_tests {
use super::*;
#[test]
fn test_something() {}
}
mod integration_tests {
use super::*;
#[test]
fn test_integration() {}
}
}
测试辅助函数 #
rust
#[cfg(test)]
mod test_utils {
pub fn create_test_data() -> TestData {
TestData::default()
}
pub fn assert_approx_eq(a: f64, b: f64, epsilon: f64) {
assert!(
(a - b).abs() < epsilon,
"{} is not approximately equal to {}",
a,
b
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::test_utils::*;
#[test]
fn test_with_helper() {
let data = create_test_data();
assert_approx_eq(data.value, 1.0, 0.001);
}
}
测试夹具 #
setup 和 teardown #
rust
#[cfg(test)]
mod tests {
use super::*;
struct TestContext {
// 测试上下文
}
impl TestContext {
fn new() -> Self {
// setup
TestContext {}
}
}
impl Drop for TestContext {
fn drop(&mut self) {
// teardown
}
}
#[test]
fn test_with_context() {
let _ctx = TestContext::new();
// 测试代码
}
}
使用 tempfile #
toml
[dev-dependencies]
tempfile = "3.0"
rust
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_with_temp_dir() {
let temp_dir = TempDir::new().unwrap();
let path = temp_dir.path();
// 使用临时目录
}
}
性能测试 #
基准测试 #
rust
// benches/my_bench.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fibonacci(n: u64) -> u64 {
match n {
0 => 1,
1 => 1,
n => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
配置 Cargo.toml #
toml
[dev-dependencies]
criterion = "0.5"
[[bench]]
name = "my_bench"
harness = false
运行基准测试 #
bash
cargo bench
测试覆盖率 #
使用 tarpaulin #
bash
# 安装
cargo install cargo-tarpaulin
# 运行
cargo tarpaulin
# 生成 HTML 报告
cargo tarpaulin --out Html
使用 llvm-cov #
bash
# 安装
cargo install cargo-llvm-cov
# 运行
cargo llvm-cov
# 生成 HTML 报告
cargo llvm-cov --html
测试最佳实践 #
1. 测试命名 #
rust
#[test]
fn test_add_returns_sum() {} // ✅ 描述性命名
#[test]
fn test1() {} // ❌ 不清晰
2. 一个测试一个断言 #
rust
#[test]
fn test_add_positive_numbers() {
assert_eq!(add(1, 2), 3);
}
#[test]
fn test_add_negative_numbers() {
assert_eq!(add(-1, -2), -3);
}
3. 使用测试辅助函数 #
rust
#[cfg(test)]
mod test_utils {
pub fn create_user(name: &str) -> User {
User::new(name, "test@example.com")
}
}
4. 测试边界条件 #
rust
#[test]
fn test_empty_input() {}
#[test]
fn test_single_element() {}
#[test]
fn test_large_input() {}
#[test]
fn test_boundary_values() {}
5. 测试错误情况 #
rust
#[test]
fn test_error_handling() {
let result = function_that_may_fail();
assert!(result.is_err());
}
常见问题 #
1. 测试超时 #
bash
# 设置超时时间
cargo test -- --test-timeout 60
2. 测试顺序 #
bash
# 并行运行(默认)
cargo test
# 串行运行
cargo test -- --test-threads=1
3. 测试输出 #
bash
# 显示 println! 输出
cargo test -- --nocapture
4. 测试依赖 #
toml
[dev-dependencies]
tempfile = "3.0"
pretty_assertions = "1.0"
mockall = "0.12"
下一步 #
最后更新:2026-03-28