Codes in lesson24.1
Codes in lesson24.2
Unit Test 测试结构 基本单元测试 使用 #[cfg(test)] 属性标记测试模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #[cfg(test)] mod tests { use super::*; #[test] fn it_works () { assert_eq! (2 + 2 , 4 ); } #[test] fn test_add () { assert_eq! (add (1 , 2 ), 3 ); } }
常用测试宏 assert! 验证布尔表达式为 true
1 2 3 4 5 #[test] fn test_assert () { let result = true ; assert! (result); }
assert_eq!比较两个值是否相等
1 2 3 4 #[test] fn test_equality () { assert_eq! ("hello" , "hello" ); }
assert_ne!比较两个值是否不相等
1 2 3 4 #[test] fn test_inequality () { assert_ne! ("foo" , "bar" ); }
should_panic测试是否如预期般 panic
1 2 3 4 5 #[test] #[should_panic(expected = "除数为零" )] fn test_divide_by_zero () { 1 / 0 ; }
测试生命周期 测试函数生命周期 使用 #[test] 标记的函数有独立的生命周期:
每个测试在自己的线程中运行 测试失败只会导致该线程崩溃,不会影响其他测试 返回值必须是 () 类型(单元类型) 测试前准备与清理 使用 setup 和 teardown 函数进行测试环境的准备和清理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #[cfg(test)] mod tests { struct TestFixture { value: i32 , } impl TestFixture { fn new () -> Self { println! ("创建测试环境" ); TestFixture { value: 42 } } } impl Drop for TestFixture { fn drop (&mut self ) { println! ("清理测试环境" ); } } #[test] fn test_with_fixture () { let fixture = TestFixture::new (); assert_eq! (fixture.value, 42 ); } }
多测试共享环境 使用生命周期更长的结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #[cfg(test)] mod tests { static mut TEST_VALUE: i32 = 0 ; #[test] fn test_one () { unsafe { TEST_VALUE = 42 }; assert_eq! (unsafe { TEST_VALUE }, 42 ); } #[test] fn test_two () { assert_eq! (unsafe { TEST_VALUE }, 0 ); } }
测试私有函数 Rust 允许在测试中访问私有函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 mod my_module { pub fn public_func () -> i32 { private_func () } fn private_func () -> i32 { 42 } #[cfg(test)] mod tests { use super::*; #[test] fn test_private_func () { assert_eq! (private_func (), 42 ); } } }
高级测试功能 忽略测试 使用 #[ignore] 标记暂时不运行的测试:
1 2 3 4 5 #[test] #[ignore = "功能尚未实现" ] fn expensive_test () { }
测试组合使用 组合多个属性:
1 2 3 4 5 6 #[test] #[ignore] #[should_panic(expected = "预期错误" )] fn complex_test () { }
数据驱动测试 使用循环或宏创建多组测试数据:
1 2 3 4 5 6 7 8 9 10 11 12 #[test] fn test_addition () { let test_cases = vec! [ (1 , 1 , 2 ), (2 , 3 , 5 ), (-1 , 1 , 0 ), (0 , 0 , 0 ), ]; for (a, b, expected) in test_cases { assert_eq! (a + b, expected); } }
测试运行与管理 运行测试 1 2 3 4 5 6 7 8 9 10 cargo test cargo test test_add cargo test -- --ignored cargo test -- --nocapture cargo test -- --test-threads=1
测试覆盖率 使用 tarpaulin 或 cargo llvm-cov 获取测试覆盖率:
1 2 3 4 cargo install cargo-tarpaulin cargo tarpaulin --ignore-tests
Integration Test Rust 集成测试 用于测试库的公共 API,验证多个模块协同工作的情况。与单元测试关注内部实现不同,集成测试从外部用户的角度验证程序功能
集成测试核心概念 位置与约定 默认放在项目根目录的 tests 目录中,每个测试文件都是独立的 crate,Cargo自动发现并运行这些测试
1 2 3 4 5 6 7 8 9 project/ ├── Cargo.toml ├── src/ │ └── lib.rs └── tests/ ├── integration_test1.rs ├── integration_test2.rs └── common/ └── mod.rs # 共享辅助代码
注意:使用 common/mod.rs 而不是 common.rs
基本特点 仅能访问公共API 测试完整工作流程 模拟外部依赖 可测试多个模块组合行为 创建集成测试 基本结构 1 2 3 4 5 6 use my_library; #[test] fn test_add () { assert_eq! (my_library::add (2 , 3 ), 5 ); }
运行集成测试 1 2 cargo test --test integration_test_name cargo test
高级集成测试技巧 共享辅助代码 在 tests/common/mod.rs 中创建共享功能:
1 2 3 4 5 6 7 8 pub fn setup () { println! ("测试环境准备" ); } pub fn teardown () { println! ("清理测试环境" ); }
在测试文件中使用:
1 2 3 4 5 6 7 8 9 10 11 mod common;use common::{setup, teardown};use my_library;#[test] fn test_complex_operation () { setup (); let result = my_library::complex_operation (); assert! (result.is_ok ()); teardown (); }
测试模块组织 1 2 3 4 5 6 pub mod v1;pub mod v2;#[test] fn test_api_v1 () { }
集成测试报告 1 2 3 4 cargo test --test integration_tests -- --format json | cargo2html -o report.html cargo tarpaulin --integration-tests --out Html
Doc Tests 文档测试通过允许将测试代码直接嵌入到文档注释中,来确保文档示例始终与代码行为保持一致,该机制极大增强了 Rust 生态系统的文档质量
基本概念 什么是文档测试? Rust 支持将可执行代码示例直接嵌入到文档注释中。当运行 cargo test 时,这些代码块会被提取、编译并作为测试运行,目标是确保文档中的示例始终正确有效
核心价值 文档与代码同步:示例代码随功能变化自动更新 - 可信文档:用户可信任通过测试的文档示例 - 开发者便利:编写文档的同时就创建了测试用例 基础语法 基本文档测试 1 2 3 4 5 6 7 8 9 10 11 pub fn add (a: i32 , b: i32 ) -> i32 { a + b }
模块级文档测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 pub fn multiply (a: i32 , b: i32 ) -> i32 { a * b }
文档测试工作机制 编译与执行流程 文档解析:rustdoc 提取文档中的代码块 2. 测试代码生成:自动添加 extern crate 语句 将代码包装到 fn main() { ... } 中 添加必要的 use 语句 3. 独立编译:每个文档测试作为独立 crate 编译 4. 测试执行:编译后作为普通测试运行 生成的测试代码(示例) 原始文档注释:
1 2 let result = add (2 , 3 );assert_eq! (result, 5 );
实际执行的测试代码:
1 2 3 4 5 6 7 8 9 10 extern crate my_crate;use my_crate::add;fn main () { let result = add (2 , 3 ); assert_eq! (result, 5 ); } #[test] fn test_name () { main (); }
高级使用技巧 隐藏辅助代码 使用 # 隐藏辅助代码(在文档中不显示,但在测试中包含):
文档展示效果:
1 2 let s = setup ();assert_eq! (s, "world" );
测试错误处理 使用 should_panic 属性测试预期 panic:
1 2 3 4 5 6 7 8 9 10 pub fn divide (a: i32 , b: i32 ) -> i32 { if b == 0 { panic! ("除零错误" ); } a / b }
编译期测试 使用 compile_fail 确保代码无法编译:
定制测试属性 通过内联属性控制测试行为:
使用返回值测试 直接在文档注释中返回值:
文档测试属性控制 Crate 级属性配置 在 lib.rs 或 main.rs 顶部全局配置:
1 2 3 4 5 6 #![doc(test(attr( // 为所有文档测试启用 unstable 特性 allow(unused_imports), feature(staged_api) )))] #![doc(test(attr(deny(warnings))))]
文档测试最佳实践 测试文档覆盖原则每个公共 API 都应该有文档测试 重点关注边界情况和错误处理 避免过度测试简单 getter/setter 性能优化策略保持示例简洁(不超过 10-15 行) 复杂逻辑拆分为多个测试 Benches Rust 的基准测试 (Benchmarks) 是用于评估代码性能的核心工具。Rust 提供了强大的基准测试框架,帮助开发者对代码性能进行定量分析和优化
基准测试概览 基准测试的目的 性能度量:测量代码执行时间 - 性能对比:比较不同实现的速度差异 - 回归检测:防止性能退化 - 优化依据:确定优化点并量化效果 基准测试的特点 精确测量:使用高精度计时器 - 统计分析:计算平均值、离群值等 - 内存分析:可集成内存分配器 - 编译器优化控制:通过 black_box 避免过度优化 原生基准测试框架(test crate) Rust 标准库提供了内置基准测试支持,但需要 nightly 版本:
1 2 3 4 5 6 7 8 9 10 #![feature(test)] extern crate test;use test::Bencher;#[bench] fn bench_add (b: &mut Bencher) { b.iter (|| { (0 ..1000 ).sum::<u64 >() }); }
关键组件 Bencher:基准测试控制器 - iter 方法:包含要测量的代码 - black_box:防止编译器优化 编译与运行 1 rustup run nightly cargo bench
输出示例:
1 test bench_add ... bench: 1,234 ns/iter (+/- 123)
Examples Rust 中的示例测试是位于项目 examples/ 目录下的可执行文件,它们作为项目的高级文档,展示如何使用库的实际应用场景。与文档测试中的小代码片段不同,示例测试提供了更完整、更真实的用法演示
项目结构与约定 标准目录结构 1 2 3 4 5 6 7 8 9 10 my_crate/ ├── Cargo.toml ├── src/ │ └── lib.rs └── examples/ ├── basic_usage.rs ├── advanced_usage.rs └── demo_setup/ ├── main.rs └── helper.rs
命名约定 文件: examples/some_example.rs 二进制名称: cargo run –example some_example 创建基础示例 1 2 3 4 5 6 7 8 9 10 11 12 use my_crate::{add, multiply};fn main () { println! ("2 + 3 = {}" , add (2 , 3 )); println! ("2 × 3 = {}" , multiply (2 , 3 )); match my_crate::divide (10 , 2 ) { Ok (result) => println! ("10 / 2 = {}" , result), Err (e) => println! ("Error: {}" , e), } }
测试与验证指令 编译验证 1 2 cargo test --examples cargo build --examples
运行测试 1 2 cargo run --example basic_demo cargo run --example network_client
多文件示例 使用目录包含多个源文件:
1 2 3 4 5 examples/ └── complex_demo/ ├── main.rs ├── network.rs └── processors.rs
在 Cargo.toml 中配置:
1 2 3 [[example]] name = "complex_demo" path = "examples/complex_demo/main.rs"
目录结构规范 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 . ├── Cargo.lock ├── Cargo.toml ├── src/ │ ├── lib.rs │ ├── main.rs │ └── bin/ │ ├── named-executable.rs │ ├── another-executable.rs │ └── multi-file-executable/ │ ├── main.rs │ └── some_module.rs ├── benches/ # 基准测试 │ ├── large-input.rs │ └── multi-file-bench/ │ ├── main.rs │ └── bench_module.rs ├── examples/ # 示例 │ ├── simple.rs │ └── multi-file-example/ │ ├── main.rs │ └── ex_module.rs └── tests/ # 集成测试 ├── some-integration-tests.rs └── multi-file-test/ ├── main.rs └── test_module.rs
课后习题 参考如下模版,创建并发布一个 crate 到 crates.io
关注点:
单元测试 继承测试 doc 测试 examples 项目规范 !()[https://github.com/ibuidl/template-lib-crate]
进阶 修改 ci.yaml,通过 github actions 自动更新 crate