赞
踩
Rust 是具备内嵌单元测试模块的。在 Rust 中,可以通过在源代码文件的顶部使用 #[test]
属性来标记一个函数作为测试函数。通常,这些测试函数位于与它们测试的源代码相同的文件中,但位于 mod tests
模块中。这是一个常用做法。
另外还有一个标记属性。#[cfg(test)]
是一个条件编译属性,它允许你编写只在测试构建中编译的代码。这对于定义只在测试时需要的辅助函数、类型或模块特别有用,从而避免在生产代码中引入不必要的开销或依赖。
创建一个测试代码:
fn im_true() -> bool { true
}
fn is_false() -> bool {
false
}
fn is_num_three() -> u32 {
3
}
fn is_string() -> String {
String::from("hello")
}
接下来测试函数 is_false 的返回是否为布尔值:否,需要先建立一个测试模块,然后建立一个测试函数
#[cfg(test)] mod test { use crate::is_false; #[test] // check if is_false return false fn check_bool_false() { let f = is_false(); assert!(!f, "is not false"); } #[test] // check if return true fn check_bool_true() { let f = is_false(); assert!(f, "is not true"); } }
上面的代码中建立了一个名字为 test 的测试模块,由于标记了#[cfg (test)]标签,所以在执行 cargo build 时是不会执行编译的,只有在执行 cargo test 时会编译执行。另外建立了两个单元测试内容:check_bool_false, check_bool_true,两个函数都用#[test]进行了标记。当执行 cargo test 时会看到两个测试项的结果。上述代码比较简单,应该是一个成功一个失败:
#cargo test ...... running 2 tests test test::check_bool_false ... ok test test::check_bool_true ... FAILED failures: ---- test::check_bool_true stdout ---- thread 'test::check_bool_true' panicked at src/main.rs:37:9: is not true note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures: test::check_bool_true test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass `--bin unitestlearn`
成功的单元测试会描述为 ok,而失败的会使用:FAILED 标记进行提示。同时会有标准输出以及总结哪些测试用例失败的描述。
上述代码中有个关键字 assert!,这个关键字是检查给定的布尔表达式是否为真。如果为假,则测试失败。类似的关键字还有很多
关键字 | 说明 |
---|---|
assert! | 检查给定的布尔表达式是否为真。如果为假,则测试失败。 |
assert_eq! | 检查两个表达式是否相等。如果不等,则测试失败 |
assert_ne! | 检查两个表达式是否不相等。如果相等,则测试失败 |
Assert_debug_snapshot! | 用于比较当前代码的调试输出是否与先前存储的快照匹配,这有助于在重构代码时确保其行为未改变 (依赖 insta ) |
以下是一些案例
#[test] ----- 布尔值检查 // check if return true fn check_bool_true() { let f = im_true(); assert!(f, "is not true"); } #[test] ---- 数字相等检查 fn check_equal_num() { assert_eq!(2,2); } #[test] ----- 数字不等检查 fn check_ne_num() { assert_ne!(3,2); } #[test] ----- 快照检查 fn check_snapshot() { let mut settings = insta::Settings::clone_current(); settings.set_snapshot_path("../test/snapshot/"); settings.bind(|| { let name = "helloworld"; insta::assert_debug_snapshot!(name); }); }
快照检查需要多说一下,写快照检查时需要引入一个依赖,所以在 Cargo. Toml 中需要增加如下内容
[dependencies]
insta = "1.38.0"
在项目目录下创建一个 test/snapshot 目录,该目录将作为快照存储的目录,代码中采用 settings.set_snapshot_path 进行设置,settings. Bind 中设置测试流程,第一次执行时测试用例会失败,并自动在快照目录下生成一个快照文件,如上面的用例会生成一个:unitestlearn__test__check_snapshot.snap. New 的文件,生成后将该文件 new 的后缀去掉后保存就作为一个快照保存即可。
在 Rust 中可以通过 macro_rules!进行声明式宏的定义。能够减少重复代码的编写,简化代码,大大提高可读性和编码效率。
以下面为例:
macro_rules! snapshot_test { ($testname:ident, $txtcontent:literal) => { #[test] fn $testname() { let mut settings = insta::Settings::clone_current(); settings.set_snapshot_path("../test/snapshot/"); settings.bind(|| { let name = $txtcontent; insta::assert_debug_snapshot!(name); }); } } } snapshot_test!(a_check, "a_check"); snapshot_test!(b_check, "b_check"); snapshot_test!(c_check, "c_check");
类似这种及其相似的代码就可以使用宏定义的方式快速实现。Macro_rules 的格式为
macro_rules! [宏定义名称] {
() => {}
// 其中括号为入参,大括号为函数体
}
针对宏定义的参数类型有以下几种
类型 | 说明 |
---|---|
ident | 标识符,如变量名或函数名。 |
block | 代码块,包括 { ... } 内的所有内容。 |
stmt | 语句,如赋值或函数调用。 |
expr | 表达式,可以求值为某个值的代码片段。 |
pat | 模式,用于匹配值或解构数据。 |
ty | 类型表达式,如 int 或 Vec<T> 。 |
lifetime | 生命周期标注,如 'a 。 |
literal | 字面量,如字符串、整数或浮点数字面量。 |
path | 路径,用于引用模块、类型或值。 |
meta | 元项,如属性(attributes)。 |
tt | 令牌树(token tree),可以包含任何有效的 Rust 令牌。 |
item | 项,可以是一个函数、结构体定义等。 |
vis | 可见性修饰符,如 pub 或 priv 。 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。