编写测试
在前端开发的时候,要做单元测试的时候,一般使用使用第三方 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)}
复制代码
评论