Rust 自动化测试

发布时间: 更新时间: 总字数:1408 阅读时间:3m 作者: IP上海 分享 网址

Rust通过编写测试函数验证代码功能是否和预期一致。

介绍

Rust 测试函数通常有三部分组成:

  • 准备数据
  • 运行被测试的代码
  • 断言 assert 结果

定义

  • 测试函数需要使用 #[test] 属性(attribute)将函数变成测试函数
  • cargo test 命令运行 Rust 项目所有的测试函数
  • cargo 创建 library 项目时,会生成一个test module,里面包含一个 test 函数
    • 创建命令:cargo new <lib-name> --libsrc/lib.rs 示例:
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        let result = 2 + 2;
        assert_eq!(result, 4);
    }
}
  • 测试失败:每个测试运行一个新线程,测试函数 panic! (触发现场挂掉)表示测试失败

断言 Assert

Rust 使用标准库中的:

  • assert!([true | false])
    • 测试结果
      • true:测试通过
      • false:调用 panic!,测试失败
    • 可以通过第2个参数自定义错误消息
  • assert_eq!(k1, k2) 宏相等(==)判断
    • 可以通过第3个参数自定义错误消息,assert_eq!(k1, k2, "msg")
  • assert_ne!(k1, k2) 宏不相等(!=)判断
    • 可以通过第3个参数自定义错误消息,assert_ne!(k1, k2, "msg")
  • assert_eq!assert_ne! 失败自动使用 debug 格式打印出两个参数的值,调用 PartialEqDebug Traits 实现输出

检测恐慌 should_panic

#[should_panic] 属性(attribute)用来检测代码是否发生恐慌:

  • 函数发生 panic:测试通过
  • 函数未发生 panic:测试不通过
fn abc(value: i32) -> i32 {
    if value > 1 {
        panic!("{} > 1", value);
    }
    value
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn it_works1() {
        abc(2);
    }

    #[test]
    #[should_panic(expected="xxxx")]
    fn it_works2() {
        abc(2);
    }
}

说明:

  • #[should_panic(expected="xxx")] 属性(attribute)添加 expected 参数检测失败消息是否包含特定的内容

测试结果 Result<T, E>

测试结果也可以使用 Result<T, E> 作为返回类型:

  • 返回 Ok:测试通过
  • 返回 Err:测试不通过
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("2+2!=4"))
        }
    }
}

说明:

  • Result<T, E> 结果的测试,不需要添加 #[should_panic] 属性,因为 Err 已经处理 panic

cargo test

cargo test 默认行为:

  • 并行运行所有测试
  • 默认不显示输出

cargo test 命令行参数:

  • 帮助:cargo test --help
  • 可执行程序参数:cargo test -- xxx
    • 测试程序二进制帮助(有编译过程):cargo test -- --help

并行运行测试

多个线程并行运行:

  • 特点:速度快
  • 要求:
    • 测试用例间不相互依赖
    • 不依赖某个共享状态(如环境、工作目录、环境变量等)
  • 线程控制:cargo test -- --test-threads n

显示函数输出

默认,测试通过不会显示 println! 的输出,失败会显示。

显示所有(成功+失败)输出:cargo test -- --show-output

按名称运行测试

  • 按名称运行测试子集:将测试的名称(一个或多个)作为 cargo test 的参数
    • 单个测试:cargo test <测试函数名>
    • 多个测试:指定测试名称的一部分(模块名称也可以)
      • cargo test <common-prefix>

忽略某些测试

  • 使用 #[ignore] 属性注释的测试用例使用 cargo test 会自动忽略
  • 使用 cargo test -- --ignored 执行忽略的测试用例
    #[ignore]
    fn test_xxx() {}

测试的分类

  • 单元测试
    • 小、专注
    • 一次对一个模块进行隔离测试
    • 可测试 private 接口/函数(和使用public函数相同)
  • 集成测试
    • 在库外部
    • 只能测试 public 接口
    • 可能在每个测试中使用到多个模块

单元测试

  • #[cfg(test)] 标注
    • 仅当运行 cargo test 时才编译和运行代码
    • 运行 cargo build 不编译和运行
    • cfg(configuration)
    • 配置项 test:由 Rust 提供,用来编译和运行测试,只有 cargo test 才编译,运行 #[test] 标注的函数

集成测试

  • 在 Rust 中,集成测试完全位于被测试库的外部

  • 目的:测试多个部分集成在一起工作是否正常

  • 重要指标:覆盖率

  • 目录:与 src 目录并列的 tests 目录

    • tests 目录下的每一个测试文件都是单独的一个 crate
    • tests/common/mod.rs 类似子目录不会被单独编译,可以在测试文件中使用 mod 导入该模块
    • 该目录仅当执行 cargo test 时编译
  • tests/xxx_test.rs

// 导入库
use abc;

#[test]
fn it_xxx() {
    assert_eq!(4, abc::add(2, 2));
}
  • 执行
cargo test
  • 运行指定的集成测试
    • 运行一个特定的集成测试:cargo test 函数名
    • 运行某个测试文件内的所有测试:cargo test --test 文件名

其他说明:

  • library crate 才能暴露函数给其他 crate 用
  • 针对 binary crate 的集成测试,只包含 src/main.rs 没有 src/lib.rs
    • 不能在 tests 目录下创建集成测试
    • 无法把 main.rs 的函数导入作用域
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数