编写测试
在前端开发的时候,要做单元测试的时候,一般使用使用第三方 jest 库,但是在 rust 开发时,rust 已经帮开发者内置了可测试功能
如何编写测试
不管在任何语言中的测试,都不外乎以下三个步骤:
准备所需的数据或状态。
调用需要测试的代码。
断言运行结果与我们所期望的一致。
测试函数的构成
将 #[test]添加到关键字 fn 的上一行便可以将函数转变为测试函数,然后使用 cargo test 命令来运行测试:
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
复制代码
在测试中如果出现 panic!会导致测试失败:
#[test]
fn another() {
panic!("出错了")
}
复制代码
assert!检查结果是否为 true:
#[test]
fn is_true() {
let is_eq = 2 > 1;
assert!(is_eq);
}
复制代码
assert_eq!检查两个数据是否相同:
#[test]
fn is_eq() {
// assert_eq!和assert_ne!宏分别使用了==和!=运算符来进行判断,
// 并在断言失败时使用调试输出格式{:?}将参数值打印出来。
// 这意味着它们的参数必须同时实现PartialEq和Debug这两个trait。
#[derive(Debug, PartialEq)]
struct Rect {
width: u32,
height: u32,
}
let rect1 = Rect {
width: 150,
height: 100,
};
let rect2 = Rect { ..rect1 };
assert_eq!(rect1, rect2);
}
复制代码
assert_ne!检查两个数据是否不相同:
#[test]
fn is_ne() {
fn add(a: i32, b: i32) -> i32 {
a + b
}
assert_ne!(add(1, 2), 4)
}
复制代码
添加自定义的错误提示信息:
#[test]
fn custom_error() {
let a = 1;
let b = 2;
// 断言函数在必要的参数之后的所有参数都将传递给format!宏,用于打印
assert!(a > b, "{}应该大于{}", a, b);
assert_eq!(a, a, "{}应该等于", a);
assert_ne!(a, b, "{}应该不等于{}", a, b);
}
复制代码
在函数上标记 should_panic 属性来断言应该触发异常:
#[test]
#[should_panic]
fn should_error() {
panic!("应该会出错")
}
复制代码
确认抛出的异常是预期的,而不是其他位置的异常:
#[test]
#[should_panic(expected = "小于")]
fn should_error() {
let a = 1;
let b = 2;
if a > b {
panic!("a大于b")
} else {
panic!("a小于b") // expected中的`小于`在这个panic!中的文字是匹配的
}
}
复制代码
使用 Result<T, E>编写测试,这种测试方式可以用来代替 assert 系列的测试方式,函数返回 Ok 时表示测试通过:
#[test]
fn use_result() -> Result<(), String> {
let a = 1;
let b = 1;
if a == b {
Result::Ok(()) // Ok的参数需要是空元组
} else {
Result::Err(format!("{} != {}", a, b))
}
}
复制代码
控制测试的运行方式
运行测试的参数:
控制测试运行方式,通过--
来分隔命令参数:
cargo 默认开启并行测试,用于减少测试时间,这就要为多个测试之前不能彼此先后依赖,可以用参数控制具体的线程数:
cargo test -- --test-threads=1
复制代码
在测试通过时,我们无法看到代码中的 println!等标注输出,因为会被 Rust 捕获,使用参数来控制 Rust 不进行捕获标注输出:
cargo test -- --nocapture
复制代码
可以只运行部分测试用例来减少测试时间,例如测试所有匹配字符串is_
的测试用例:
运行单个测试:
注意:不能运行多个测试:
cargo test it_works is_eq
复制代码
通过显式指定来忽略某些测试:
#[test]
#[ignore]
fn should_ignore() {
panic!("这个错误不会被抛出,因为should_ignore测试不会被运行")
}
复制代码
如果想要只运行这些被忽略的测试:
测试组织结构
单元测试
单元测试一般和业务代码都写在同一文件中,只是通过新建一个测试模块来标识:
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// 标记为测试的模块将会在cargo build后移除
// 另外,我们不需要对集成测试标注#[cfg(test)],因为集成测试本身就放置在独立的目录中,rust自然清楚build的时候不进行处理。
// cfg: configuration,这里的意思是只有在test时才会被处理
#[cfg(test)]
mod testaaa {
use super::add;
#[test]
pub fn test_add() {
assert_eq!(add(1, 2), 3)
}
}
复制代码
集成测试
集成测试是完全位于 src 目录之外的 tests 文件夹中:
测试一个外部包 adder:
use adder::add_one;
#[test]
fn test_add() {
assert_eq!(add_one(1), 2)
}
复制代码
测试(包含单元测试、集成测试、文档测试) cargo test:
只运行某个集成测试:
cargo test --test [集成测试文件名]
复制代码
集成测试公共函数需要放置在 tests/common/mod.rs 文件中,否则放置在其他位置也都将被 rust 识别成集成测试用例:
mod common; // 引用使用公共包
#[test]
fn test_add() {
// 使用公共包中的setup函数
common::setup();
assert_eq!(add_one(1), 2)
}
复制代码
评论