Rust内置了强大的测试框架和文档生成工具。无需额外依赖,就能编写单元测试、集成测试和文档测试,并用cargo doc生成漂亮的API文档。
// 测试函数标注 #[test]
// 通常放在同一文件中,用 #[cfg(test)] 模块包裹
fn add(a: i32, b: i32) -> i32 { a + b }
fn multiply(a: i32, b: i32) -> i32 { a * b }
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 { Err("除零错误".to_string()) } else { Ok(a / b) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
assert_eq!(add(-1, 1), 0);
assert_eq!(add(0, 0), 0);
}
#[test]
fn test_multiply() {
assert_eq!(multiply(3, 4), 12);
assert_eq!(multiply(-2, 3), -6);
}
#[test]
fn test_divide_ok() {
assert_eq!(divide(10.0, 2.0), Ok(5.0));
}
#[test]
fn test_divide_zero() {
assert!(divide(1.0, 0.0).is_err());
assert_eq!(divide(1.0, 0.0), Err("除零错误".to_string()));
}
#[test]
#[should_panic(expected = "overflow")]
fn test_overflow() {
// 测试应该panic的情况
let _ = i32::MAX + 1; // debug模式会panic
}
#[test]
fn test_ignore() -> Result<(), String> {
// 使用?运算符的测试
let result = divide(10.0, 3.0)?;
assert!((result - 3.333).abs() < 0.01);
Ok(())
}
}
fn main() {
println!("运行测试: cargo test");
println!("测试单个: cargo test test_add");
println!("显示输出: cargo test -- --show-output");
println!("忽略测试: cargo test -- --ignored");
}
✅ 验证通过
/// 计算两个整数的和
///
/// # 参数
/// - `a` - 第一个加数
/// - `b` - 第二个加数
///
/// # 返回值
/// 两个数的和
///
/// # 示例
/// ```
/// let result = mylib::add(2, 3);
/// assert_eq!(result, 5);
/// ```
fn add(a: i32, b: i32) -> i32 { a + b }
//! 模块级文档(在文件开头)
//! 这个库提供了数学计算工具
// 生成文档: cargo doc
// 打开文档: cargo doc --open
pub struct Calculator {
history: Vec<String>,
}
impl Calculator {
pub fn new() -> Self { Calculator { history: Vec::new() } }
pub fn add(&mut self, a: f64, b: f64) -> f64 {
let result = a + b;
self.history.push(format!("{} + {} = {}", a, b, result));
result
}
pub fn subtract(&mut self, a: f64, b: f64) -> f64 {
let result = a - b;
self.history.push(format!("{} - {} = {}", a, b, result));
result
}
pub fn multiply(&mut self, a: f64, b: f64) -> f64 {
let result = a * b;
self.history.push(format!("{} * {} = {}", a, b, result));
result
}
pub fn divide(&mut self, a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 { return Err("不能除零".into()); }
let result = a / b;
self.history.push(format!("{} / {} = {}", a, b, result));
Ok(result)
}
pub fn history(&self) -> &[String] { &self.history }
pub fn clear(&mut self) { self.history.clear(); }
}
fn main() {
let mut calc = Calculator::new();
println!("2 + 3 = {}", calc.add(2.0, 3.0));
println!("10 / 4 = {:?}", calc.divide(10.0, 4.0));
println!("历史: {:?}", calc.history());
}
✅ 验证通过
先写测试,再实现函数:is_palindrome(s: &str) -> bool
为排序算法编写基准测试,比较冒泡排序和内置排序的性能。
fn fibonacci(n: u32) -> u64 {
let mut a = 0u64; let mut b = 1u64;
for _ in 0..n { let t = a + b; a = b; b = t; }
a
}
fn is_prime(n: u64) -> bool {
if n < 2 { return false; }
if n < 4 { return true; }
if n % 2 == 0 || n % 3 == 0 { return false; }
let mut i = 5;
while i * i <= n { if n % i == 0 || n % (i+2) == 0 { return false; } i += 6; }
true
}
fn main() {
println!("fib(50) = {}", fibonacci(50));
let primes: Vec = (2..100).filter(|&n| is_prime(n)).collect();
println!("100以内素数({}个): {:?}", primes.len(), primes);
println!("运行测试: cargo test");
println!("文档测试: cargo test --doc");
println!("基准测试: cargo bench");
}
✅ 验证通过
// 测试金字塔
// / E2E \
// / 集成测试 \
// / 单元测试 \
// /_______________\
// 单元测试: 测试单个函数/方法
// 集成测试: tests/目录,测试模块间交互
// E2E测试: 测试完整用户流程
// 测试辅助工具
fn fibonacci(n: u32) -> u64 {
let mut a = 0u64; let mut b = 1u64;
for _ in 0..n { let t = a + b; a = b; b = t; }
a
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fibonacci_base() {
assert_eq!(fibonacci(0), 0);
assert_eq!(fibonacci(1), 1);
assert_eq!(fibonacci(2), 1);
}
#[test]
fn test_fibonacci_values() {
assert_eq!(fibonacci(10), 55);
assert_eq!(fibonacci(20), 6765);
}
// 参数化测试模式
#[test]
fn test_fibonacci_table() {
let cases = [(0,0),(1,1),(5,5),(10,55),(20,6765)];
for (input, expected) in cases {
assert_eq!(fibonacci(input), expected, "fib({})", input);
}
}
}
fn main() {
println!("测试金字塔: 单元 > 集成 > E2E");
println!("运行: cargo test");
println!("详细: cargo test -- --show-output");
println!("单个: cargo test test_fibonacci_base");
println!("并行: cargo test -- --test-threads=4");
println!("文档测试: cargo test --doc");
}
/// 计算斐波那契数
///
/// # 参数
/// * `n` - 非负整数
///
/// # 返回
/// 第n个斐波那契数
///
/// # Panics
/// 当n过大导致u64溢出时
///
/// # Examples
/// ```
/// use mylib::fibonacci;
/// assert_eq!(fibonacci(10), 55);
/// ```
fn fibonacci(n: u32) -> u64 { /* ... */ }
// cargo doc --open 生成并打开文档
// 文档包含: 搜索、模块树、源码链接
pub struct Stack {
items: Vec,
}
impl Stack {
pub fn new() -> Self { Stack { items: Vec::new() } }
pub fn with_capacity(capacity: usize) -> Self {
Stack { items: Vec::with_capacity(capacity) }
}
pub fn push(&mut self, item: T) { self.items.push(item); }
pub fn pop(&mut self) -> Option { self.items.pop() }
pub fn peek(&self) -> Option<&T> { self.items.last() }
pub fn peek_mut(&mut self) -> Option<&mut T> { self.items.last_mut() }
pub fn len(&self) -> usize { self.items.len() }
pub fn is_empty(&self) -> bool { self.items.is_empty() }
pub fn clear(&mut self) { self.items.clear(); }
pub fn into_vec(self) -> Vec { self.items }
}
impl Stack {
pub fn peek_all(&self) -> &[T] { &self.items }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_stack() {
let s: Stack = Stack::new();
assert!(s.is_empty());
assert_eq!(s.len(), 0);
}
#[test]
fn test_push_pop() {
let mut s = Stack::new();
s.push(1); s.push(2); s.push(3);
assert_eq!(s.len(), 3);
assert_eq!(s.pop(), Some(3));
assert_eq!(s.pop(), Some(2));
assert_eq!(s.peek(), Some(&1));
}
#[test]
fn test_empty_pop() {
let mut s: Stack = Stack::new();
assert_eq!(s.pop(), None);
}
#[test]
fn test_peek_mut() {
let mut s = Stack::new();
s.push(10);
if let Some(top) = s.peek_mut() { *top += 5; }
assert_eq!(s.pop(), Some(15));
}
#[test]
fn test_clear() {
let mut s = Stack::new();
s.push(1); s.push(2);
s.clear();
assert!(s.is_empty());
}
}
fn main() {
let mut stack = Stack::new();
for i in 1..=5 { stack.push(i * 10); }
println!("栈内容: {:?}", stack.peek_all());
while let Some(v) = stack.pop() { print!("{} ", v); }
println!();
}
✅ 验证通过
# .github/workflows/ci.yml
# name: Rust CI
# on: [push, pull_request]
# jobs:
# test:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - run: rustup toolchain install stable
# - run: cargo build --verbose
# - run: cargo test --verbose
# - run: cargo clippy -- -D warnings
# - run: cargo fmt --check
# - run: cargo doc --no-deps
fn main() {
println!("CI最佳实践:");
println!(" 1. 每次提交都运行测试");
println!(" 2. clippy检查代码质量");
println!(" 3. rustfmt检查代码格式");
println!(" 4. cargo doc确保文档完整");
println!(" 5. 覆盖率: cargo tarpaulin");
println!(" 6. 安全审计: cargo audit");
}
// 属性测试(Property-based Testing)
// 不测试具体值,而是测试属性(规律)
fn reverse_string(s: &str) -> String { s.chars().rev().collect() }
fn sort_vec(v: &mut Vec) { v.sort(); }
fn main() {
// 属性1: 反转两次等于原值
println!("属性测试: 反转两次 = 原值");
for test in ["hello", "Rust🦀", "abba", ""] {
let double_reversed = reverse_string(&reverse_string(test));
assert_eq!(test, double_reversed, "反转两次应等于原值");
}
println!(" ✅ 通过");
// 属性2: 排序后长度不变
println!("属性测试: 排序后长度不变");
for _ in 0..10 {
let mut v: Vec = (0..20).map(|_| rand_val()).collect();
let len_before = v.len();
sort_vec(&mut v);
assert_eq!(v.len(), len_before);
}
println!(" ✅ 通过");
// 属性3: 排序后有序
println!("属性测试: 排序后有序");
let mut v = vec![5, 2, 8, 1, 9];
sort_vec(&mut v);
for i in 0..v.len()-1 { assert!(v[i] <= v[i+1]); }
println!(" ✅ {:?} 有序", v);
}
fn rand_val() -> i32 { (std::time::SystemTime::now().elapsed().unwrap().subsec_nanos() % 100) as i32 }
✅ 验证通过