Rust个人学习之单元测试

基础标记说明

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 类型表达式,如 intVec<T>
lifetime 生命周期标注,如 'a
literal 字面量,如字符串、整数或浮点数字面量。
path 路径,用于引用模块、类型或值。
meta 元项,如属性(attributes)。
tt 令牌树(token tree),可以包含任何有效的 Rust 令牌。
item 项,可以是一个函数、结构体定义等。
vis 可见性修饰符,如 pubpriv
相关推荐
Han.miracle1 小时前
数据结构——排序的学习(一)
java·数据结构·学习·算法·排序算法
电子云与长程纠缠2 小时前
Blender入门学习01
学习·blender
qiuiuiu4132 小时前
正点原子RK3568学习日志12-注册字符设备
linux·开发语言·单片机·学习·ubuntu
_dindong3 小时前
Linux网络编程:Socket编程TCP
linux·服务器·网络·笔记·学习·tcp/ip
金士顿3 小时前
ethercat网络拓扑详细学习
学习
知识分享小能手4 小时前
uni-app 入门学习教程,从入门到精通,uni-app组件 —— 知识点详解与实战案例(4)
前端·javascript·学习·微信小程序·小程序·前端框架·uni-app
wahkim4 小时前
Flutter 学习资源及视频
学习
小白要努力sgy5 小时前
待学习--中间件
学习·中间件
~无忧花开~5 小时前
CSS学习笔记(五):CSS媒体查询入门指南
开发语言·前端·css·学习·媒体
吴鹰飞侠5 小时前
AJAX的学习
前端·学习·ajax