5 releases (3 breaking)
new 0.4.2 | Mar 24, 2023 |
---|---|
0.4.1 | Mar 17, 2023 |
0.3.0 | Jan 31, 2023 |
0.2.0 | Dec 27, 2022 |
0.1.1 | Dec 9, 2022 |
#49 in Testing
174 downloads per month
Used in 2 crates
380KB
6.5K
SLoC
GoogleTest Rust
This library brings the rich assertion types of Google's C++ testing library GoogleTest to Rust. It provides:
- A framework for writing matchers which can be combined to make a wide range of assertions on data,
- A rich set of matchers providing similar functionality to those included in GoogleTest, and
- A new set of assertion macros offering similar functionality to those of GoogleTest.
The minimum supported Rust version is 1.67
⚠️ The API is not fully stable and may still be changed until we publish version 1.0.
Assertions and matchers
Most assertions are made through the macro verify_that!
which evaluates to a
Result<()>
. It takes two arguments: an actual value to be tested and a
Matcher
.
Unlike the macros used in other test assertion libraries in Rust, verify_that!
does not panic when the test assertion fails. Instead, it evaluates to Result
,
which the caller can choose to handle by:
- Returning immediately from the function with the
?
operator (a fatal assertion), or - Logging the failure, marking the test as failed, and allowing execution to continue (see Non-fatal assertions below).
Fatal assertions are analogous to the ASSERT_*
family of macros in GoogleTest.
For example, for fatal assertions:
use googletest::{matchers::eq, verify_that, Result};
#[test]
fn more_than_one_failure() -> Result<()> {
let value = 2;
verify_that!(value, eq(4))?; // Fails and ends execution of the test.
verify_that!(value, eq(2)) // One can also just return the assertion result.
}
In case one wants behaviour closer to other Rust test libraries, the macro
assert_that!
has the same parameters asverify_that!
but panics on failure.
This library includes a rich set of matchers, covering:
- Equality, numeric inequality, and approximate equality;
- Strings and regular expressions;
- Containers and set-theoretic matching.
Matchers are composable:
use googletest::{matchers::{contains, ge}, verify_that, Result};
#[test]
fn contains_at_least_one_item_at_least_3() -> Result<()> {
let value = vec![1, 2, 3];
verify_that!(value, contains(ge(3)))
}
They can also be logically combined:
use googletest::{matchers::{gt, lt, not, AndMatcherExt}, verify_that, Result};
#[test]
fn strictly_between_9_and_11() -> Result<()> {
let value = 10;
verify_that!(value, gt(9).and(not(ge(11))))
}
Pattern-matching
One can use the macro matches_pattern!
to create a composite matcher for a
struct or enum that matches fields with other matchers:
use googletest::{
matchers::{contains_substring, eq, gt},
matches_pattern,
verify_that,
Result,
};
struct AStruct {
a_field: i32,
another_field: i32,
a_third_field: &'static str,
}
#[test]
fn struct_has_expected_values() -> Result<()> {
let value = AStruct {
a_field: 10,
another_field: 100,
a_third_field: "A correct value",
};
verify_that!(value, matches_pattern!(AStruct {
a_field: eq(10),
another_field: gt(50),
a_third_field: contains_substring("correct"),
}))
}
Writing matchers
One can extend the library by writing additional matchers. To do so, create a
struct holding the matcher's data and have it implement the trait Matcher
:
struct MyEqMatcher<T> {
expected: T,
}
impl<T: PartialEq + Debug> Matcher<T> for MyEqMatcher<T> {
fn matches(&self, actual: &A) -> MatcherResult {
(self.expected == *actual).into()
}
fn describe(&self, matcher_result: MatcherResult) -> String {
match matcher_result {
MatcherResult::Matches => {
format!("is equal to {:?} the way I define it", self.expected)
}
MatcherResult::DoesNotMatch => {
format!("isn't equal to {:?} the way I define it", self.expected)
}
}
}
}
It is recommended to expose a function which constructs the matcher:
pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<T> {
MyEqMatcher { expected }
}
The new matcher can then be used in verify_that!
:
#[test]
fn should_be_equal_by_my_definition() -> Result<()> {
verify_that!(10, eq_my_way(10))
}
Non-fatal assertions
Using non-fatal assertions, a single test is able to log multiple assertion failures. Any single assertion failure causes the test to be considered having failed, but execution continues until the test completes or otherwise aborts.
This is analogous to the EXPECT_*
family of macros in GoogleTest.
To make a non-fatal assertion, use the macro expect_that!
. The test must
also be marked with google_test
instead of the Rust-standard #[test]
. It
must return Result<()>
.
use googletest::{
expect_that, google_test, matchers::eq, Result
};
#[google_test]
fn more_than_one_failure() -> Result<()> {
let value = 2;
expect_that!(value, eq(3)); // Just marks the test as having failed.
verify_that!(value, eq(2))?; // Passes, but the test already failed.
Ok(())
}
Interoperability
You can use the #[google_test]
macro together with many other libraries such
as rstest. Just apply both attribute macros
to the test:
#[google_test]
#[rstest]
#[case(1)]
#[case(2)]
#[case(3)]
fn rstest_works_with_google_test(#[case] value: u32) -> Result<()> {
verify_that!(value, gt(0))
}
Make sure to put #[google_test]
before #[rstest]
. Otherwise the annotated
test will run twice, since both macros will attempt to register a test with the
Rust test harness.
The macro also works together with async tests with Tokio in the same way:
#[google_test]
#[tokio::test]
async fn should_work_with_tokio() -> Result<()> {
verify_that!(3, gt(0))
}
There is one caveat when running async tests: test failure reporting through
and_log_failure
will not work properly if the assertion occurs on a different
thread than runs the test.
Predicate assertions
The macro verify_pred!
provides predicate assertions analogous to
GoogleTest's EXPECT_PRED
family of macros. Wrap an invocation of a predicate
in a verify_pred!
invocation to turn that into a test assertion which passes
precisely when the predicate returns 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))?;
The assertion failure message shows the arguments and the values to which they evaluate:
stuff_is_correct(x, y) was false with
x = 3,
y = 4
The verify_pred!
invocation evaluates to a Result<()>
just like
verify_that!
. There is also a macro expect_pred!
to make a non-fatal
predicaticate assertion.
Unconditionally generating a test failure
The macro fail!
unconditionally evaluates to a Result
indicating a test
failure. It can be used analogously to verify_that!
and verify_pred!
to
cause a test to fail, with an optional formatted message:
#[test]
fn always_fails() -> Result<()> {
fail!("This test must fail with {}", "today")
}
Contributing Changes
Please read CONTRIBUTING.md for details on how to contribute to this project.
Dependencies
~1.7–2.5MB
~66K SLoC