
9 测试
测试的泛式
无论是单元测试还是集成测试,都拥有一样的测试泛式。我们需要测试的只是中间的环境,但是在测试之前需要准备测试数据以及预期结果。
单元测试
rust可以将测试代码直接写在源文件中,通常我们会创建额外的模块来收纳测试函数。
只在运行cargo test的时候编译运行测试代码。
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
#[cfg(test)]属性标记,条件编译,这样标志之后,被标记的模块只会在运行测试的时候编译。不会被编译进debug以及发型版。
#[test]属性标记测试方法,函数必须没有参数,返回值必须是 ()或Result<(), E>。
被标记的测试方法,将被编译进一个测试用的二进制crate中用于测试。
我们可以用过测试函数的全路径匹配来执行指定路径的测试函数,所有路径包含次字符串的测试函数都将执行。
cargo test <匹配关键词>
# 或者将匹配项放在 -- 后面
cargo test -- <匹配关键词>
cargo test以及cargo的其他命令都有命令参数分段的设定。我们需要对cargo传入一些参数,也需要对cargo调用的二进制运行项目一些运行参数,这两部分参数需要同时进行传递,所以为了区分,将cargo的命令参数放在前面,而后面对要调用的二进制程序的参数放在 — 参数之后(实际上我们知道2_python解释器)
忽略测试函数,以及包含忽略。
打印测试输入。
断言结果
assert!(true或者false)
assert_eq!(left,right) 判断是否相等
检测Result<(),E>,检测Panic!
#[should_panic]
当判断一个函数应该报panic的时候,可以在函数头上标注#[should_panic]
match!(模式1,模式2)
这个宏进行模式匹配并返回bool
条件编译
只有满足这些条件,这些代码才会被编译。
#[cfg(test)] 在测试环境
#[cfg(target_os = "android")] 在android编译
#[cfg(not(target_os = "android"))] 在非android编译
控制测试运行
并发执行测试函数,以及设置并发数量。
集成测试
只能调用一部分库中的公有 API。集成测试的目的是测试库的多个部分能否一起正常工作。
在项目根目录创建一个 tests 目录,与 src 同级。Cargo 会去寻找这个目录中的集成测试文件。
集成测试目录中的文件,自动被编译为单独的crate,也就是Cargo默认让我们以文件为单位写集成测试。
需要将库引入到每个测试 crate 的作用域中,就像其他外部使用者一样,这点区别于单元测试,因为单元测试是直接在源码内部的测试,无须导入。
不需要将 tests/integration_test.rs 中的任何代码标注为 #[cfg(test)] 。 tests 文件夹在 Cargo 中是一个特殊的文件夹,Cargo 只会在运行 cargo test 时编译这个目录中的文件。
执行cargo test后,会依次执行单元测试、集成测试、文档测试。
注意如果一个部分的任何测试失败,之后的部分都不会运行。
避免出现因为一个错误导致集成测试连带失败的问题,我们应该先解决单元测试再解决集成测试问题。
Rust给集成测试弄了特殊的语法,可以使用旧的模块语法来表示多crate共享的模块。
实际上并非共享模块,而是让“共享模块”的代码分别编译进每个测试二进制文件中,来实现类似共享的效果。
只有库 crate才会向其他 crate 暴露了可供调用和使用的函数;二进制 crate 只意在单独运行。
但是我们可以让二进制crate调用lib crate来实现,我们对lib crate进行测试就行了,这就是许多 Rust 二进制项目使用一个简单的 src/main.rs 调用 src/lib.rs 中的逻辑的原因之一。
性能测试
性能测试虽然官方推出了nightly版本,但是其功能还是不如专用性能测试工具 criterion。
安装之后cargo通过 cargo bench启动,寻找bench目录下的性能测试crate。
通过cargo.toml配置性能测试用例名,通过名字定位文件位置。
