9

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?

ForeverLearning
  • 6,352
  • 3
  • 27
  • 33
  • 1
    Can you not place a `gtest` assertion macro in a `catch` block? – A.E. Drew Sep 12 '13 at 21:31
  • @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 commenting – ForeverLearning Sep 13 '13 at 13:51
  • 1
    There is a complete answer there: http://stackoverflow.com/questions/23270078/test-a-specific-exception-type-is-thrown-and-the-exception-has-the-right-propert – Germain Chazot Aug 26 '16 at 09:19
  • 1
    http://stackoverflow.com/questions/29506426/get-google-test-exception-throw-message – voltento Mar 22 '17 at 07:21

2 Answers2

3

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();
}
Florian Winter
  • 4,750
  • 1
  • 44
  • 69
A.E. Drew
  • 2,097
  • 1
  • 16
  • 24
  • 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. – ForeverLearning Sep 13 '13 at 13:55
  • 9
    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. – Mr. Boy Dec 18 '13 at 15:47
  • @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? – dashesy Jun 30 '16 at 16:27
  • You could add a `FAIL()` in the try block after the instruction supposed to throw an exception. – ymoreau May 15 '20 at 08:22
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.

Florian Winter
  • 4,750
  • 1
  • 44
  • 69