引言:为什么 Rust 中的测试简单又可靠
Rust 从设计之初就是一门帮助编写安全可靠代码的语言。然而,即使是最严格的类型系统也无法保证业务逻辑的正确性。这正是测试发挥作用的地方。在 Rust 中,编写测试的过程直接内置于 cargo 工具链中,使其变得异常便捷和易用。
在本文中,我们将探讨三个测试层级:unit(单元测试)、integration(集成测试)和 e2e(端到端测试)。你将学习如何编写测试、何时使用哪种方法以及如何自动化代码验证。本文内容对新手和转向 Rust 的有经验开发者都很有帮助。
Unit 测试:从内部进行测试
Unit 测试(单元测试)是最基本且最常用的测试类型。它们独立于系统其余部分,对单个函数、方法或模块进行测试。在 Rust 中,通常将 unit 测试直接写在与被测代码相同的文件中,或者写在模块内单独的 tests.rs 文件中。
如何在 Rust 中编写 unit 测试
创建测试使用 #[test] 属性。测试放在带有 #[cfg(test)] 属性的模块中,该属性确保测试代码不会进入最终构建。
// src/lib.rs
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(String::from("Division by zero"))
} else {
Ok(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]
fn test_divide_success() {
let result = divide(10.0, 2.0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 5.0);
}
#[test]
fn test_divide_by_zero() {
let result = divide(10.0, 0.0);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "Division by zero");
}
}
运行 unit 测试
使用一条命令即可运行测试:
cargo test
输出将显示有多少测试通过,多少测试失败。要运行特定测试,请使用过滤器:
cargo test test_add
Unit 测试非常适合检查边界情况、错误处理和算法的正确性。它们速度快且不需要外部依赖。
Integration 测试:检查模块间的交互
集成测试检查库或应用程序的不同模块如何协同工作。与 unit 测试不同,它们无法访问内部函数(除非这些函数被导出为 pub)。集成测试写在项目根目录下单独的 tests/ 文件夹中。
集成测试的结构
在项目根目录创建 tests/ 文件夹,并在其中添加测试文件。每个文件都将作为独立的 crate 进行编译。
// tests/integration_test.rs
use my_crate; // 你的 crate 名称
#[test]
fn test_add_integration() {
assert_eq!(my_crate::add(10, 20), 30);
}
#[test]
fn test_divide_integration() {
let result = my_crate::divide(100.0, 4.0);
assert_eq!(result.unwrap(), 25.0);
}
测试的公共模块
如果多个集成测试使用相同的辅助函数,请将它们提取到公共模块中。为此,创建文件夹 tests/common/mod.rs:
// tests/common/mod.rs
pub fn setup() {
// 初始化日志记录器、数据库等
println!("Setting up test environment...");
}
// tests/another_test.rs
mod common;
#[test]
fn test