单元测试
学习目标
- 掌握
#[test]属性 - 掌握断言宏
- 理解测试组织结构
核心概念
基本测试
// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_add_negative() {
assert_eq!(add(-1, 1), 0);
}
#[test]
#[should_panic]
fn test_panic() {
panic!("expected panic");
}
#[test]
fn test_with_result() -> Result<(), String> {
let result = add(2, 2);
if result == 4 {
Ok(())
} else {
Err(format!("expected 4, got {}", result))
}
}
}
断言宏
#[cfg(test)]
mod tests {
#[test]
fn assertions() {
assert!(true); // 真值断言
assert_eq!(2 + 2, 4); // 相等断言
assert_ne!(2 + 2, 5); // 不等断言
// 带消息
assert!(true, "should be true");
assert_eq!(2 + 2, 4, "math works");
// 浮点数
assert!((0.1_f64 + 0.2 - 0.3).abs() < 1e-10);
}
}
运行测试
cargo test # 运行所有测试
cargo test test_add # 运行名字包含 test_add 的测试
cargo test -- --show-output # 显示 println! 输出
cargo test -- --test-threads=1 # 单线程运行
cargo test --lib # 只运行库测试
实践练习
练习 1:栈测试
pub struct Stack<T> {
elements: Vec<T>,
}
impl<T> Stack<T> {
pub fn new() -> Self {
Stack { elements: Vec::new() }
}
pub fn push(&mut self, item: T) {
self.elements.push(item);
}
pub fn pop(&mut self) -> Option<T> {
self.elements.pop()
}
pub fn peek(&self) -> Option<&T> {
self.elements.last()
}
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_stack_is_empty() {
let stack: Stack<i32> = Stack::new();
assert!(stack.is_empty());
}
#[test]
fn test_push_and_pop() {
let mut stack = Stack::new();
stack.push(1);
stack.push(2);
assert_eq!(stack.pop(), Some(2));
assert_eq!(stack.pop(), Some(1));
assert_eq!(stack.pop(), None);
}
#[test]
fn test_peek() {
let mut stack = Stack::new();
stack.push(42);
assert_eq!(stack.peek(), Some(&42));
assert_eq!(stack.pop(), Some(42));
assert_eq!(stack.peek(), None);
}
}
小结
| 语法 | 用途 |
|---|---|
#[test] | 标记测试函数 |
#[cfg(test)] | 条件编译测试模块 |
assert! | 真值断言 |
assert_eq! | 相等断言 |
#[should_panic] | 期望 panic |
cargo test | 运行测试 |
练习编辑器
rust
Loading...