You can build your own assertion that allows you to make assertions on the thrown expected exception:
- In a helper function, catch the exception, make assertions about it, then re-throw it.
- 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
.
gtest
assertion macro in acatch
block? – Cochrane