跳到主要内容

文档测试(Documentation Tests)

文档测试是 Rust 独有的特性,它允许在文档注释中编写可执行的代码示例。这确保了文档中的代码示例始终是正确和最新的。

基本文档测试

简单示例

/// 将两个数字相加
///
/// # Examples
///
/// ```
/// use my_crate::add;
/// assert_eq!(add(2, 3), 5);
/// assert_eq!(add(-1, 1), 0);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}

/// 计算数字的平方
///
/// # Examples
///
/// ```
/// use my_crate::square;
/// assert_eq!(square(4), 16);
/// assert_eq!(square(-3), 9);
/// assert_eq!(square(0), 0);
/// ```
pub fn square(x: i32) -> i32 {
x * x
}

/// 检查数字是否为偶数
///
/// # Examples
///
/// ```
/// use my_crate::is_even;
/// assert!(is_even(4));
/// assert!(!is_even(3));
/// assert!(is_even(0));
/// ```
pub fn is_even(n: i32) -> bool {
n % 2 == 0
}

运行文档测试

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

# 运行所有测试(包括文档测试)
cargo test

# 生成文档并运行文档测试
cargo doc --open

高级文档测试

多行示例

/// 一个简单的计算器结构体
///
/// # Examples
///
/// ```
/// use my_crate::Calculator;
///
/// let mut calc = Calculator::new();
/// calc.add(10);
/// calc.multiply(2);
/// calc.subtract(5);
///
/// assert_eq!(calc.result(), 15);
/// ```
pub struct Calculator {
value: i32,
}

impl Calculator {
/// 创建新的计算器
///
/// # Examples
///
/// ```
/// use my_crate::Calculator;
/// let calc = Calculator::new();
/// assert_eq!(calc.result(), 0);
/// ```
pub fn new() -> Self {
Calculator { value: 0 }
}

/// 加法操作
///
/// # Examples
///
/// ```
/// use my_crate::Calculator;
/// let mut calc = Calculator::new();
/// calc.add(5);
/// assert_eq!(calc.result(), 5);
///
/// calc.add(3);
/// assert_eq!(calc.result(), 8);
/// ```
pub fn add(&mut self, n: i32) {
self.value += n;
}

/// 乘法操作
///
/// # Examples
///
/// ```
/// use my_crate::Calculator;
/// let mut calc = Calculator::new();
/// calc.add(5);
/// calc.multiply(3);
/// assert_eq!(calc.result(), 15);
/// ```
pub fn multiply(&mut self, n: i32) {
self.value *= n;
}

/// 减法操作
///
/// # Examples
///
/// ```
/// use my_crate::Calculator;
/// let mut calc = Calculator::new();
/// calc.add(10);
/// calc.subtract(3);
/// assert_eq!(calc.result(), 7);
/// ```
pub fn subtract(&mut self, n: i32) {
self.value -= n;
}

/// 获取当前结果
///
/// # Examples
///
/// ```
/// use my_crate::Calculator;
/// let calc = Calculator::new();
/// assert_eq!(calc.result(), 0);
/// ```
pub fn result(&self) -> i32 {
self.value
}
}

错误处理示例

use std::num::ParseIntError;

/// 解析字符串为整数
///
/// # Examples
///
/// 成功的情况:
/// ```
/// use my_crate::parse_int;
/// assert_eq!(parse_int("42"), Ok(42));
/// assert_eq!(parse_int("-10"), Ok(-10));
/// assert_eq!(parse_int("0"), Ok(0));
/// ```
///
/// 错误的情况:
/// ```
/// use my_crate::parse_int;
/// assert!(parse_int("not_a_number").is_err());
/// assert!(parse_int("").is_err());
/// assert!(parse_int("12.34").is_err());
/// ```
pub fn parse_int(s: &str) -> Result<i32, ParseIntError> {
s.parse()
}

/// 安全除法,避免除零错误
///
/// # Examples
///
/// ```
/// use my_crate::safe_divide;
///
/// // 正常除法
/// assert_eq!(safe_divide(10, 2), Ok(5));
/// assert_eq!(safe_divide(7, 3), Ok(2));
///
/// // 除零错误
/// assert_eq!(safe_divide(10, 0), Err("除零错误"));
/// ```
pub fn safe_divide(a: i32, b: i32) -> Result<i32, &'static str> {
if b == 0 {
Err("除零错误")
} else {
Ok(a / b)
}
}

文档测试属性

隐藏代码行

/// 格式化用户信息
///
/// # Examples
///
/// ```
/// # use my_crate::User;
/// # use my_crate::format_user;
/// let user = User::new("Alice", 30);
/// let formatted = format_user(&user);
/// assert_eq!(formatted, "Alice (30 years old)");
/// ```
pub fn format_user(user: &User) -> String {
format!("{} ({} years old)", user.name, user.age)
}

pub struct User {
pub name: String,
pub age: u32,
}

impl User {
pub fn new(name: &str, age: u32) -> Self {
User {
name: name.to_string(),
age,
}
}
}

忽略文档测试

/// 这个函数需要网络连接
///
/// # Examples
///
/// ```ignore
/// use my_crate::fetch_data;
/// let data = fetch_data("https://api.example.com/data").await?;
/// println!("Data: {}", data);
/// ```
pub async fn fetch_data(url: &str) -> Result<String, Box<dyn std::error::Error>> {
// 实际的网络请求代码
Ok("mock data".to_string())
}

/// 这个函数在某些平台上不可用
///
/// # Examples
///
/// ```no_run
/// use my_crate::system_specific_function;
/// system_specific_function();
/// ```
pub fn system_specific_function() {
// 系统特定的代码
println!("This might not work on all systems");
}

编译但不运行

/// 展示 API 用法但不实际运行
///
/// # Examples
///
/// ```compile_fail
/// use my_crate::private_function;
/// private_function(); // 这会编译失败,因为函数是私有的
/// ```
///
/// ```no_run
/// use my_crate::expensive_operation;
/// expensive_operation(); // 编译通过但不运行
/// ```
pub fn expensive_operation() {
// 耗时操作
std::thread::sleep(std::time::Duration::from_secs(10));
}

fn private_function() {
println!("This is private");
}

复杂示例

状态机文档测试

/// 一个简单的状态机
///
/// # Examples
///
/// 基本状态转换:
/// ```
/// use my_crate::{StateMachine, State};
///
/// let mut machine = StateMachine::new();
/// assert_eq!(machine.current_state(), State::Idle);
///
/// machine.start();
/// assert_eq!(machine.current_state(), State::Running);
///
/// machine.stop();
/// assert_eq!(machine.current_state(), State::Stopped);
///
/// machine.reset();
/// assert_eq!(machine.current_state(), State::Idle);
/// ```
///
/// 错误状态转换:
/// ```should_panic
/// use my_crate::StateMachine;
///
/// let mut machine = StateMachine::new();
/// machine.stop(); // 从 Idle 状态直接停止会 panic
/// ```
#[derive(Debug, PartialEq)]
pub enum State {
Idle,
Running,
Stopped,
}

pub struct StateMachine {
state: State,
}

impl StateMachine {
pub fn new() -> Self {
StateMachine { state: State::Idle }
}

pub fn current_state(&self) -> &State {
&self.state
}

pub fn start(&mut self) {
match self.state {
State::Idle | State::Stopped => self.state = State::Running,
State::Running => {} // 已经在运行
}
}

pub fn stop(&mut self) {
match self.state {
State::Running => self.state = State::Stopped,
State::Idle => panic!("不能从空闲状态直接停止"),
State::Stopped => {} // 已经停止
}
}

pub fn reset(&mut self) {
self.state = State::Idle;
}
}

集合操作文档测试

use std::collections::HashMap;

/// 用户管理器
///
/// # Examples
///
/// 完整的用户管理流程:
/// ```
/// use my_crate::{UserManager, User};
///
/// let mut manager = UserManager::new();
///
/// // 添加用户
/// let user1 = User::new("Alice", "alice@example.com");
/// let user2 = User::new("Bob", "bob@example.com");
///
/// let id1 = manager.add_user(user1);
/// let id2 = manager.add_user(user2);
///
/// // 查找用户
/// let found_user = manager.find_user(id1).unwrap();
/// assert_eq!(found_user.name, "Alice");
///
/// // 更新用户
/// manager.update_user_email(id1, "newalice@example.com").unwrap();
/// let updated_user = manager.find_user(id1).unwrap();
/// assert_eq!(updated_user.email, "newalice@example.com");
///
/// // 列出所有用户
/// let all_users = manager.list_users();
/// assert_eq!(all_users.len(), 2);
///
/// // 删除用户
/// assert!(manager.remove_user(id2));
/// assert_eq!(manager.list_users().len(), 1);
/// ```
pub struct UserManager {
users: HashMap<u32, User>,
next_id: u32,
}

#[derive(Debug, Clone)]
pub struct User {
pub name: String,
pub email: String,
}

impl User {
pub fn new(name: &str, email: &str) -> Self {
User {
name: name.to_string(),
email: email.to_string(),
}
}
}

impl UserManager {
pub fn new() -> Self {
UserManager {
users: HashMap::new(),
next_id: 1,
}
}

pub fn add_user(&mut self, user: User) -> u32 {
let id = self.next_id;
self.users.insert(id, user);
self.next_id += 1;
id
}

pub fn find_user(&self, id: u32) -> Option<&User> {
self.users.get(&id)
}

pub fn update_user_email(&mut self, id: u32, new_email: &str) -> Result<(), &'static str> {
if let Some(user) = self.users.get_mut(&id) {
user.email = new_email.to_string();
Ok(())
} else {
Err("用户不存在")
}
}

pub fn remove_user(&mut self, id: u32) -> bool {
self.users.remove(&id).is_some()
}

pub fn list_users(&self) -> Vec<&User> {
self.users.values().collect()
}
}

文档测试最佳实践

1. 提供完整的使用示例

/// HTTP 客户端包装器
///
/// # Examples
///
/// 基本用法:
/// ```no_run
/// use my_crate::HttpClient;
///
/// # tokio_test::block_on(async {
/// let client = HttpClient::new("https://api.example.com");
///
/// // GET 请求
/// let response = client.get("/users").await?;
/// println!("Users: {}", response);
///
/// // POST 请求
/// let data = r#"{"name": "Alice", "email": "alice@example.com"}"#;
/// let response = client.post("/users", data).await?;
/// println!("Created user: {}", response);
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// # });
/// ```
pub struct HttpClient {
base_url: String,
}

impl HttpClient {
pub fn new(base_url: &str) -> Self {
HttpClient {
base_url: base_url.to_string(),
}
}

pub async fn get(&self, path: &str) -> Result<String, Box<dyn std::error::Error>> {
// 实际实现
Ok("mock response".to_string())
}

pub async fn post(&self, path: &str, data: &str) -> Result<String, Box<dyn std::error::Error>> {
// 实际实现
Ok("mock response".to_string())
}
}

2. 测试边界条件

/// 查找数组中的最大值
///
/// # Examples
///
/// 正常情况:
/// ```
/// use my_crate::find_max;
/// assert_eq!(find_max(&[1, 3, 2, 5, 4]), Some(&5));
/// assert_eq!(find_max(&[10]), Some(&10));
/// ```
///
/// 边界情况:
/// ```
/// use my_crate::find_max;
/// assert_eq!(find_max(&[]), None);
/// assert_eq!(find_max(&[-1, -5, -3]), Some(&-1));
/// ```
pub fn find_max(arr: &[i32]) -> Option<&i32> {
arr.iter().max()
}

3. 展示错误处理

/// 配置解析器
///
/// # Examples
///
/// 成功解析:
/// ```
/// use my_crate::parse_config;
///
/// let config_str = r#"
/// name = "MyApp"
/// version = "1.0.0"
/// debug = true
/// "#;
///
/// let config = parse_config(config_str).unwrap();
/// assert_eq!(config.name, "MyApp");
/// assert_eq!(config.version, "1.0.0");
/// assert_eq!(config.debug, true);
/// ```
///
/// 错误处理:
/// ```
/// use my_crate::parse_config;
///
/// let invalid_config = "invalid config format";
/// assert!(parse_config(invalid_config).is_err());
/// ```
#[derive(Debug, PartialEq)]
pub struct Config {
pub name: String,
pub version: String,
pub debug: bool,
}

pub fn parse_config(config_str: &str) -> Result<Config, &'static str> {
// 简化的解析逻辑
if config_str.contains("name") && config_str.contains("version") {
Ok(Config {
name: "MyApp".to_string(),
version: "1.0.0".to_string(),
debug: true,
})
} else {
Err("配置格式无效")
}
}

运行和调试文档测试

测试命令

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

# 运行特定模块的文档测试
cargo test --doc my_module

# 显示文档测试输出
cargo test --doc -- --nocapture

# 运行被忽略的文档测试
cargo test --doc -- --ignored

# 生成文档并测试
cargo doc --document-private-items

调试文档测试

/// 调试文档测试
///
/// # Examples
///
/// ```
/// use my_crate::debug_function;
///
/// let result = debug_function(42);
/// println!("调试输出: {}", result); // 使用 --nocapture 查看输出
/// assert_eq!(result, 84);
/// ```
pub fn debug_function(x: i32) -> i32 {
println!("输入值: {}", x);
let result = x * 2;
println!("结果: {}", result);
result
}

文档测试是 Rust 独特而强大的特性,它确保文档中的代码示例始终正确,提高了代码的可维护性和文档的可信度。下一节我们将学习性能测试和基准测试。