Verifying exception messages with GoogleTest
Asked Answered
P

2

10

Is it possible to verify the message thrown by an exception? Currently one can do:

ASSERT_THROW(statement, exception_type)

which is all fine and good but no where can I find a way to test e.what() is really what I am looking for. Is this not possible via google test?

Perpetual answered 12/9, 2013 at 21:20 Comment(4)
Can you not place a gtest assertion macro in a catch block?Cochrane
@A.E.Drew What are you talking about? Where did I mention anything about placing any macro in a catch block? Please don't comment for the sake of commentingPerpetual
There is a complete answer there: #23270578Manhole
#29506926Furore
C
4

Something like the following will work. Just catch the exception somehow and then do EXPECT_STREQ on the what() call:

#include "gtest/gtest.h"

#include <exception>

class myexception: public std::exception
{
  virtual const char* what() const throw()
  {
    return "My exception happened";
  }
} myex;


TEST(except, what)
{
  try {
    throw myex;
    FAIL();  // exception not thrown as expected
  } catch (std::exception& ex) {
      EXPECT_STREQ("My exception happened", ex.what());
  }

}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
Cochrane answered 12/9, 2013 at 21:59 Comment(4)
While I appreciate your answer very much, I knew how to do it other ways (and have been doing it for a while now). I just wanted to know if I am missing some kind of extension to the ASSERT_THROW macro that allows me to check e.what() within the macro itself. I didn't want to be writing the try/catch handlers myself.Perpetual
This is a bad approach. It does not test the exception gets thrown, it only checks the right message is seen if the exception is thrown.Echopraxia
@Mr.Boy so what is a good approach? Can you test that it is thrown and also the message is what is expected? Many error paths may throw runtime_error, how can I test that a certain runtime error is thrown without changing the code just for the sake of tests?Biodegradable
You could add a FAIL() in the try block after the instruction supposed to throw an exception.Leopoldoleor
N
0

You can build your own assertion that allows you to make assertions on the thrown expected exception:

  1. In a helper function, catch the exception, make assertions about it, then re-throw it.
  2. In an assertion function, call the helper function wrapped in EXPECT_THROW.
template<typename ExceptionT, typename ActionF, typename ExceptionMatcher>
void ExpectThrowThatHelper(ActionF action, ExceptionMatcher&& exceptionMatcher)
{
    try
    {
        action();
    }
    catch (const ExceptionT& e)
    {
        EXPECT_THAT(e, std::forward<ExceptionMatcher>(exceptionMatcher));
        throw;
    }
}

template<typename ExceptionT, typename ActionF, typename ExceptionMatcher>
void ExpectThrowThat(ActionF action, ExceptionMatcher&& exceptionMatcher)
{
    EXPECT_THROW(ExpectThrowThatHelper<ExceptionT>(std::forward<ActionF>(action), std::forward<ExceptionMatcher>(exceptionMatcher)), ExceptionT);
}

This approach applies a matcher to the exception using EXPECT_THAT. You could also just pass a function and call it, but since matchers can be built from lambdas, I find it more elegant to support matchers.

You can use this directly as follows:

struct GivenException final : std::exception
{
    int Value = 0;

    explicit GivenException(const int value)
        : Value(value)
    {}

    [[nodiscard]] const char* what() const noexcept override
    {
        return "GivenException";
    }
};

TEST(ExceptionInspectionTest, SomeCode_ThrowsGivenExceptionWithSpecificValue)
{
    using testing::Field;
    using testing::Eq;

    ExpectThrowThat<GivenException>([]
        {
            throw GivenException(123);
        }, Field(&GivenException::Value, Eq(123)));
}

If you like preprocessor macros:

#define EXPECT_THROW_THAT(ACTION, EXCEPTION, MATCHER) (ExpectThrowThat<EXCEPTION>([]{ACTION;}, (MATCHER)))

TEST(ExceptionInspectionTest, SomeCode_ThrowsGivenExceptionWithSpecificValue)
{
    using testing::Field;
    using testing::Eq;

    EXPECT_THROW_THAT(throw GivenException(123), GivenException, Field(&GivenException::Value, Eq(123)));
}

Likewise, you can write ASSERT_THROW_THAT, which uses ASSERT_THROW and ASSERT_THAT.

Natatorium answered 9/3, 2023 at 9:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.