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
相关推荐
limengshi1383921 小时前
通信工程学习:什么是IP网际协议
网络·网络协议·学习·tcp/ip·信息与通信
IM_DALLA3 小时前
【Verilog学习日常】—牛客网刷题—Verilog快速入门—VL70
学习·fpga开发·verilog学习
布丁不叮早起枣祈3 小时前
10.6学习
学习
望森FPGA3 小时前
HDLBits中文版,标准参考答案 | 3.1.2 Multiplexers | 多路复用器
学习·fpga开发
985小水博一枚呀3 小时前
【深度学习基础模型】稀疏自编码器 (Sparse Autoencoders, SAE)详细理解并附实现代码。
人工智能·python·深度学习·学习·sae·autoencoder
小魏冬琅3 小时前
命题逻辑与谓词逻辑 - 离散数学系列(二)
学习
9毫米的幻想4 小时前
【C++】—— 继承(上)
c语言·开发语言·jvm·c++·学习
mljy.4 小时前
C++《string》
c++·学习
xs_20125 小时前
Python selenium库学习使用实操二
python·学习·selenium
xiandong206 小时前
240930_CycleGAN循环生成对抗网络
图像处理·人工智能·深度学习·神经网络·学习·生成对抗网络