跳到主要内容

测试策略

问题

Rust 的测试体系是怎样的?

答案

单元测试

// 在同一文件中,用 #[cfg(test)] 模块
pub fn add(a: i32, b: i32) -> i32 {
a + b
}

pub fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err("除数不能为零".into())
} else {
Ok(a / b)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}

#[test]
fn test_divide_success() {
assert!((divide(10.0, 3.0).unwrap() - 3.333).abs() < 0.001);
}

#[test]
fn test_divide_by_zero() {
assert!(divide(10.0, 0.0).is_err());
}

#[test]
#[should_panic(expected = "溢出")]
fn test_overflow() {
let _ = (i32::MAX as i64 + 1) as i32; // 可能 panic
panic!("溢出");
}
}

集成测试

tests/api_test.rs
// tests/ 目录下的文件是独立的 crate
use my_lib::add;

#[test]
fn integration_test() {
assert_eq!(add(1, 1), 2);
}

文档测试

/// 计算两个数的最大公约数
///
/// # Examples
///
/// ```
/// use my_lib::gcd;
/// assert_eq!(gcd(12, 8), 4);
/// assert_eq!(gcd(7, 13), 1);
/// ```
pub fn gcd(mut a: u64, mut b: u64) -> u64 {
while b != 0 {
let t = b;
b = a % b;
a = t;
}
a
}
// cargo test 会自动运行文档中的代码示例

测试层级

层级位置作用
单元测试#[cfg(test)] mod tests测试私有函数
集成测试tests/ 目录测试公共 API
文档测试`/// ``` ... ````保证文档示例正确

常见面试问题

Q1: 如何在 Rust 中做 Mock?

答案

// 方式 1:trait + 手动 mock
#[cfg(test)]
struct MockDb;

#[cfg(test)]
impl Database for MockDb {
fn query(&self, _: &str) -> Vec<Row> { vec![] }
}

// 方式 2:mockall crate
use mockall::automock;

#[automock]
trait Database {
fn query(&self, sql: &str) -> Vec<Row>;
}

#[test]
fn test_with_mock() {
let mut mock = MockDatabase::new();
mock.expect_query()
.returning(|_| vec![]);
}

Q2: #[cfg(test)] 有什么作用?

答案

#[cfg(test)] 标记的代码只在 cargo test 时编译,不会出现在发布构建中。这样测试辅助代码和 mock 不会增加产物大小。

相关链接