#matcher #assertions #unit #unit-testing #test-framework #test-macro

googletest

受 GoogleTest 启发的 C++ 测试库的丰富断言和匹配器库

14 个版本 (破坏性)

0.12.0 2024年8月2日
0.11.0 2024年1月12日
0.10.0 2023年8月25日
0.9.0 2023年7月14日
0.2.0 2022年12月27日

#15 in 测试

Download history 6440/week @ 2024-05-03 7155/week @ 2024-05-10 6221/week @ 2024-05-17 6150/week @ 2024-05-24 7898/week @ 2024-05-31 7251/week @ 2024-06-07 5864/week @ 2024-06-14 6403/week @ 2024-06-21 5523/week @ 2024-06-28 5551/week @ 2024-07-05 5799/week @ 2024-07-12 6789/week @ 2024-07-19 7526/week @ 2024-07-26 9700/week @ 2024-08-02 7983/week @ 2024-08-09 6529/week @ 2024-08-16

每月下载量 33,207
用于 14 个 crate(直接使用10个)

Apache-2.0

550KB
9K SLoC

GoogleTest Rust

crates.io docs.rs Apache licensed Build Status

此库将 Google 的 C++ 测试库 GoogleTest 的丰富断言类型引入 Rust。它提供

  • 一个用于编写匹配器的框架,可以组合使用以对数据进行各种断言
  • 一套丰富的匹配器,提供类似于 GoogleTest 中包含的匹配器的功能
  • 一套新的断言宏,提供类似于 GoogleTest 的功能

最低支持的 Rust 版本是 1.66.

⚠️ API 仍处于不稳定状态,在发布 1.0 版本之前可能会发生变化。

此外,任何以 __(双下划线)开头的项目或模块都不应直接使用。这些项目或模块仅供内部使用,其 API 可能会更改,而无需进行主要版本更新。

学习资源

如果您刚开始使用 googletest,可以考虑阅读"Rust 应用高级测试"的第一个章节,这是一门自学 Rust 课程:它提供了一个关于库的引导介绍,包括练习,帮助您熟悉 googletest 宏、其匹配器及其整体理念。

断言和匹配器

GoogleTest 的核心是其 匹配器。匹配器表明正在断言实际值的一个方面:(不)相等性、包含、正则表达式匹配等。

要使用匹配器进行断言,GoogleTest 提供了三个宏

  • assert_that! 如果断言失败,则会引发恐慌,终止测试。
  • expect_that! 会记录断言失败,将测试标记为失败,但允许测试继续运行(称为非致命断言)。它需要在测试本身上使用 googletest::test 属性宏。
  • verify_that! 没有副作用,并评估为 Result<()> 类型,其 Err 变体描述了断言失败,如果有的话。结合 ? 操作符,可以在不引发恐慌的情况下在断言失败时终止测试。这也是上面两个宏的基础。

例如

use googletest::prelude::*;

#[test]
fn fails_and_panics() {
    let value = 2;
    assert_that!(value, eq(4));
}

#[googletest::test]
fn two_logged_failures() {
    let value = 2;
    expect_that!(value, eq(4)); // Test now failed, but continues executing.
    expect_that!(value, eq(5)); // Second failure is also logged.
}

#[test]
fn fails_immediately_without_panic() -> Result<()> {
    let value = 2;
    verify_that!(value, eq(4))?; // Test fails and aborts.
    verify_that!(value, eq(2))?; // Never executes.
    Ok(())
}

#[test]
fn simple_assertion() -> Result<()> {
    let value = 2;
    verify_that!(value, eq(4)) // One can also just return the last assertion.
}

该库包含了一套丰富的匹配器,涵盖

  • 等价性、数值不等式和近似等价性;
  • 字符串和正则表达式;
  • 容器和集合论匹配。

匹配器是可组合的

use googletest::prelude::*;

#[googletest::test]
fn contains_at_least_one_item_at_least_3() {
    let value = vec![1, 2, 3];
    expect_that!(value, contains(ge(3)));
}

它们也可以进行逻辑组合

use googletest::prelude::*;

#[googletest::test]
fn strictly_between_9_and_11() {
    let value = 10;
    expect_that!(value, gt(9).and(not(ge(11))));
}

模式匹配

可以使用宏 matches_pattern! 创建一个复合匹配器,用于匹配结构体或枚举的字段与其他匹配器

use googletest::prelude::*;

struct AStruct {
    a_field: i32,
    another_field: i32,
    a_third_field: &'static str,
}

#[test]
fn struct_has_expected_values() {
    let value = AStruct {
        a_field: 10,
        another_field: 100,
        a_third_field: "A correct value",
    };
    expect_that!(value, matches_pattern!(AStruct {
        a_field: eq(10),
        another_field: gt(50),
        a_third_field: contains_substring("correct"),
    }));
}

编写匹配器

可以通过编写额外的匹配器来扩展库。为此,创建一个包含匹配器数据的结构体,并使其实现 Matcher 特性

struct MyEqMatcher<T> {
    expected: T,
}

impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
    fn matches(&self, actual: T) -> MatcherResult {
         (self.expected == actual).into()
    }

    fn describe(&self, matcher_result: MatcherResult) -> String {
        match matcher_result {
            MatcherResult::Match => {
                format!("is equal to {:?} the way I define it", self.expected)
            }
            MatcherResult::NoMatch => {
                format!("isn't equal to {:?} the way I define it", self.expected)
            }
        }
    }
}

建议公开一个构造匹配器的函数

pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<T> {
    MyEqMatcher { expected }
}

然后,新匹配器可以用于断言宏

#[googletest::test]
fn should_be_equal_by_my_definition() {
    expect_that!(10, eq_my_way(10));
}

非致命断言

使用非致命断言,单个测试能够记录多次断言失败。任何单个断言失败都会导致测试被认为是失败的,但执行将继续,直到测试完成或终止。

这类似于GoogleTest中的 EXPECT_* 宏系列。

要创建非致命断言,请使用宏 expect_that!。测试还必须使用 googletest::test 而不是Rust标准的 #[test]

use googletest::prelude::*;

#[googletest::test]
fn three_non_fatal_assertions() {
    let value = 2;
    expect_that!(value, eq(2));  // Passes; test still considered passing.
    expect_that!(value, eq(3));  // Fails; logs failure and marks the test failed.
    expect_that!(value, eq(4));  // A second failure, also logged.
}

这可以在与 verify_that! 相同的测试中使用,在这种情况下,测试函数还必须返回 Result<()>

use googletest::prelude::*;

#[googletest::test]
fn failing_non_fatal_assertion() -> Result<()> {
    let value = 2;
    expect_that!(value, eq(3));  // Just marks the test as having failed.
    verify_that!(value, eq(2))?;  // Passes, so does not abort the test.
    Ok(())        // Because of the failing expect_that! call above, the
                  // test fails despite returning Ok(())
}
use googletest::prelude::*;

#[googletest::test]
fn failing_fatal_assertion_after_non_fatal_assertion() -> Result<()> {
    let value = 2;
    verify_that!(value, eq(3))?; // Fails and aborts the test.
    expect_that!(value, eq(3));  // Never executes, since the test already aborted.
    Ok(())
}

互操作性

您可以使用 #[googletest::test] 宏与其他许多库(如 rstest)一起使用。只需将两个属性宏都应用到测试上即可

#[googletest::test]
#[rstest]
#[case(1)]
#[case(2)]
#[case(3)]
fn rstest_works_with_google_test(#[case] value: u32) -> Result<()> {
   verify_that!(value, gt(0))
}

请确保在 #[googletest::test] 之前 放置 #[rstest]。否则,由于两个宏都会尝试将测试注册到Rust测试框架中,标记的测试将运行两次。

该宏还可以与 使用Tokio的异步测试 以相同的方式一起使用

#[googletest::test]
#[tokio::test]
async fn should_work_with_tokio() -> Result<()> {
    verify_that!(3, gt(0))
}

当运行异步测试时有一个需要注意的地方:如果断言发生在运行测试的不同线程上,则通过 and_log_failure 的测试失败报告将无法正常工作。

谓词断言

verify_pred! 提供了类似于 GoogleTest 的 EXPECT_PRED 宏家族的谓词断言。将谓词调用包裹在 verify_pred! 调用中,将其转换为当谓词返回 true 时才通过的测试断言。

fn stuff_is_correct(x: i32, y: i32) -> bool {
    x == y
}

let x = 3;
let y = 4;
verify_pred!(stuff_is_correct(x, y))?;

断言失败消息将显示参数及其评估的值

stuff_is_correct(x, y) was false with
  x = 3,
  y = 4

verify_pred! 调用将评估为一个 Result<()>,就像 verify_that! 一样。还有一个宏 expect_pred! 可以进行非致命的谓词断言。

无条件生成测试失败

fail! 无条件评估为一个表示测试失败的 Result。它可以类似于 verify_that!verify_pred! 使用,以导致测试失败,并可选择格式化的消息。

#[test]
fn always_fails() -> Result<()> {
    fail!("This test must fail with {}", "today")
}

配置

该库可以通过环境变量进行配置。由于配置不影响测试是否失败,而只影响失败的显示方式,我们建议在个人的 ~/.cargo/config.toml 中设置这些变量,而不是在项目范围的 Cargo.toml 中。

配置变量列表

变量名 描述
NO_COLOR 禁用彩色输出。请参阅 https://no-color.org/
FORCE_COLOR 即使输出被重定向到文件,也强制使用颜色。

提交更改

请阅读 CONTRIBUTING.md 了解如何为该项目做出贡献的详细信息。

依赖关系

~2.6–4.5MB
~83K SLoC