Skip to content

#[gtest]: print full error chain if test function returns an error #657

Open
@rursprung

Description

@rursprung

consider the following code:

#[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:

#[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 rust-lang/api-guidelines#210 and esp. the thread(s) linked there

note that i have to use #[gtest] because i want/need to use expect_that

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions