google / google/googletest-rust
`#[gtest]`: print full error chain if test function returns an error
- Dominant language
- Rust
- Stars
- 432
- Forks
- 36
- Avg merge
- 7d 17h
- Merged PRs (30d)
- 2
Description
consider the following code:
```rust
#[derive(Debug)]
enum ErrorA {
Foo,
}
impl std::fmt::Display for ErrorA {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "foo")
}
}
impl std::error::Error for ErrorA {}
#[derive(Debug)]
enum ErrorB {
Bar(ErrorA),
}
impl std::fmt::Display for ErrorB {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "bar")
}
}
impl std::error::Error for ErrorB {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ErrorB::Bar(e) => Some(e),
}
}
}
fn foo() -> Result<(), ErrorB> {
Err(ErrorB::Bar(ErrorA::Foo))
}
#[cfg(test)]
mod tests {
use super::*;
use googletest::prelude::*;
#[gtest]
fn test_foo() -> Result<()> {
foo()?;
// some googletest assertions here
Ok(())
}
}
```
this will just print:
```
---- foobar::tests::test_foo stdout ----
bar
at foo/src/bar.rs:42:9
```
compare this to when `anyhow` is used:
```rust
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Context;
#[test]
fn test_foo() -> anyhow::Result<()> {
foo()?;
// some googletest assertions here
Ok(())
}
}
```
where the output is:
```
Error: bar
Caused by:
foo
```
the problem is: `googletest` is not compatible with `anyhow`: if you annotate the method with `#[gtest]` and then return `anyhow::Result` it doesn't behave as it would without `anyhow` and thus still doesn't print the "caused by" section.
the same can be observed with the `color-eyre` library (unsurprising since it's a fork of `anyhow`).
i see two options:
* `googletest` adds support to return `anyhow::Result` from a test and then print that
* `googletest` automatically prints the "caused by" chain if a test method returns something which implements `std::error::Error`
personally i'd vote for the latter as it removes a dependency, should be pretty straight forward (just recurse over the `source` of the error until it returns `None`).
this would massively help in understanding nested errors being returned by tests (or rather: functions being called by tests).
note that best practices in rust are that errors either implement `source` _or_ print the source error in their own message. frameworks like `anyhow` use `source` and it seems that a lot of the ecosystem is doing this. see https://github.com/rust-lang/api-guidelines/pull/210 and esp. the thread(s) linked there
note that i have to use `#[gtest]` because i want/need to use `expect_that`
Contributor guide
Assessment
This issue has not been assessed yet.