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格式输出参数,因此被比较的值必须包括PartialEq和Debug这两个特性。这两个特性普遍为原生类型与标准库中的类型所拥有。而对于自定义类型则须在定义前面加上
#[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文件夹下的测试函数时,建议将其放入单独的文件夹中(?)。
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号