Rust基础学习笔记(二):自动测试

本文介绍Rust的自动测试机制。

*本篇有大量的迷惑内容,将在之后的学习求证过程中修正。

如何编写测试

测试是一些用来确定代码正常运行的函数。通过对一些数值运算后得到预期的结果来实现。

Rust中可以用test属性、一些宏和should_panic属性编写。

分析Test函数

在测试函数前面一行应有声明#[test],如下:

//File name: src/lib.rs
#[cfg(test)]
mod tests {
    #[test]
    fn it_works(){
        assert_eq!(2 + 2, 4);
    }
}
$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.72s
     Running target/debug/deps/adder-92948b65e88960b4

running 2 tests
test tests::another ... FAILED  //显示another失败
test tests::exploration ... ok  //显示exploaration成功

failures:

---- tests::another stdout ----
thread 'main' panicked at 'Make this test fail', src/lib.rs:10:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


failures:
    tests::another  //仅显示失败信息,最终编译不通过

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--lib'

 这个信息在编译行输入 $cargo test 时输出,表明只有在这种情况下才会编译 #[test] 标记的内容

还有类似measure test、Doc-tests 的内容,留作以后讨论

assert!宏的使用

assert!的参数是一个布尔表达式,如果返回了true,测试通过并无事发生;而若返回了false,测试失败导致报错。

如下:

#[test]
    fn smaller_cannot_hold_larger() {
        let larger = Rectangle    {
            width: 8,
            height: 7,
        };
        let smaller = Rectangle {
            width:5,
            height:1,
        }
        assert!(!smaller.can_hold(&larger));
)

 

预期值(expected)--> 左值(left)

实际值(actual)  -->右值(right)

同时有assert_eq!()相等与assert_ne!()不等的差别。后者用于排除非法情况。

这些宏通过debug格式输出参数,因此被比较的值必须包括PartialEqDebug这两个特性。这两个特性普遍为原生类型与标准库中的类型所拥有。而对于自定义类型则须在定义前面加上

#[derive(ParitalEq, Debug)]

 同时可以通过添加参数来对测试失败时的返回值进行自定义:

#[test]
fn greeting_contains_name(){
    let result = greeting("Carol");
    assert!(
        result.contains("Carol"),
        "Greeting didn't contain name, value was `{}`",
        result
     );
}

 这个时候就会在"panicked at"字段显示自定义的内容

should_panic的使用

简而言之就是把一个应该引起panic的函数加上对应的属性,使得其在正常编译的时候反而报错。这个属性可以拥有自己的值,在只有引起相应异常的时候才予以通过:

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    #[should_panic(expected = "Guess too Large!"]
    fn greater_than_100(){
        Guess::new(200);
    }
}

 在测试函数中运用Result<T, E>作为返回值

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("two plus two does not equal four"))
        }
    }
}

 

测试模式

可以通过在 cargo test 后面添加参数来自定义一些属性。

同时/连续运行所有测试

默认情况下会同时运行所有测试,而在一些情况下同时运行会出现错误(如读写文件)。这个时候可以尝试连续运行。

$cargo test -- --test-threads=1

 只会使用一个线程进行测试,从而保证不同的测试之间不会相互影响。

显示通过了的测试的输出

默认情况下,如果测试通过,终端只会显示其已编译通过,而不会显示其他内容,包括其中的打印函数。而测试不通过时则会显示这些内容。

若想显示通过了的测试的输出,则在运行测试时要这样写:

$ cargo test -- --show-output

 

运行指定名称的测试

单个测试

并不是任何时候都要运行所有测试。当只需要运行其中一个时,只要将目标的名称作为参数即可:

$ cargo test name_of_target_func

利用过滤器运行多个测试

只要在参数中包含多个目标名称的共同部分,就可以特定这几个测试用来运行。由于模块名称本身是测试名称的一部分,因此也可以用来做过滤器。比如以下代码

$ cargo test add

 

便可以运行形如add_two_and_two和add_four_and_two之类的函数。

默认被忽略的测试

拥有#[ignore]属性的测试默认情况下会被忽略。主要用于一些很费时间而不常用到的测试。

#[test]
#[ignore]
fn expensive_test() {
}

 

这些测试在默认情况下会被忽略,进入ignored计数。若想单独运行这些测试,可以采用:

$ cargo test -- --ignored

测试函数的组织方法

测试可以分为两种:模块级测试和工程级测试。前者可以调试私有接口,后者则完全模拟外部代码。

模块级测试

用来独立测试特定的代码。一般直接作为test模块写进/src下的对应文件中。作为模块需要有 cfg(test) 注释。该注释表明只有当你运行测试时这些代码才会编译运行。

测试还允许直接对私有函数进行测试。

工程级测试

工程级测试代码完全独立于库,只能通过公有API进行测试。需要tests目录以运行测试。

该目录直接设在工程文件夹下,与src同级。Cargo会在这个文件夹下寻找测试代码。

在工程级测试中,由于与源代码处于不同的crate中,应使用use方法。在模块级测试中不用。

use adder;

#[test]
fn it_adds_two() {
    assert_eq!(4, adder::add_two(2));
}

 

如果想运行特定的工程级测试,则要在控制台中输入:

$ cargo test --test integration_test_name

工程级测试中的子模块

(这一段并没有读懂)

在组织test文件夹下的测试函数时,建议将其放入单独的文件夹中(?)。

 

posted @ 2020-08-04 21:05  风坞  阅读(650)  评论(0)    收藏  举报