Rust学习_6.测试

测试

一、如何编写测试

  1. 准备所需的数据或状态;
  2. 调用需要测试的代码;
  3. 断言运行结果与我们期望的一致;

二、测试函数的构成

  • 测试:一个标注有test属性的函数;
  • 每个测试运行时都处于独立的线程中,主线程在监视测试线程时,一旦发现测试线程意外终止,就会将对应的测试标记为失败。

三、测试宏

  • assert! : 确保测试中某些条件的值为true;
  • assert_eq!assert_ne判断相等性;
    • 方法的参数必须同时实现PartialEqDebug 两个trait.
  • should_panic:确认代码是否按照预期处理错误。
    • 添加可选参数expected:检查panic发生时输出的错误提示信息是否包含了指定的文字。

四、添加自定义的错误提示信息

自定义的错误提示信息可以很方便地记录当前断言的含义,更容易知道代码出了什么问题。

1
2
3
4
5
#[test]
fn greeting_contains_name(){
let result = greeting("Carol");
assert!(result.contains("Carol"),"Greeting did not contain name, value was {}",result);
}

五、在测试中使用 Result<T,E>

  • 无需panic,可使用Result<T,E>作为返回类型编写测试:
    • 返回OK:测试通过;
    • 返回Err:测试失败;
  • 注意:不要在使用Result<T,E>编写的测试上标注#[should_panic]
1
2
3
4
5
6
7
8
#[test]
fn result_test()->Result<(),String> {
if 2+2==4{
Ok(())
}else{
Err(String::from("tow plus tow does not equal four"))
}
}

六、控制测试如何运行

  • 改变 cargo test的行为:添加命令行参数
  • 默认行为:
    • 并行运行
    • 所有测试
    • 成功不显示println!

1. 命令行参数

  • 针对cargo test 的参数:紧跟cargo test
    • cargo test --help
  • 针对测试可执行程序:放在--之后
    • cargo test -- --help
    • cargo test -- --show-output
    • cargo test -- --test-threads

2. 忽略某些测试,运行剩余测试

  • ignore属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #[test]
    #[ignore]
    fn result_test()->Result<(),String> {
    if 2+2==4{
    Ok(())
    }else{
    Err(String::from("tow plus tow does not equal four"))
    }
    }

3. 运行被忽略的测试:

  • cargo test -- --ignored

七、测试组织

  • Rust 对测试的分类:
    • 单元测试
    • 集成测试

1. 单元测试

  • 小、专注
  • 一次对一个模块进行隔离测试
  • 可以测试private接口

2. 集成测试

  • 在库外部。和其他外部代码一样使用你的代码
  • 只能使用public接口
  • 可能在每个测试中使用到多个模块
  • 目的:测试被测试库的多个部分是否能正确的一起工作
  • 集成测试的覆盖率很重要
2.1 tests 目录

创建和src同级的目录tests,cargo会自动在这个目录下寻找集成测试文件。

cargo在编译时会将tests目录下的每个文件的都处理为一个独立的包crate。

2.2 运行指定的集成测试
  • cargo test 函数名:运行一个特定的集成测试
  • cargo test –test 文件名:运行某个测试文件内的所有测试

3. 针对二进制包的集成测试

  • 如果项目是 binary crate,只含有 src/main.rs 没有 src/lib.rs:

    • 不能在tests目录下创建集成测试;
    • 无法把main.rs的函数导入作用域;
  • 只有library crate才能暴露函数给其他 crate 用

这就是Rust的二进制项目经常会把逻辑编写在src/lib.rs文件中,而只在src/main.rs文件中进行简单调用的原因。