I'm using Catch2 to write my unit tests.

One thing I want to do is make sure that I'm catching the correct exception. I throw the same exceptions in many circumstances, so just knowing that I'm catching an std::logic_error doesn't prove that a specific instance of the exception was indeed caught.

Catch2 provides the REQUIRE_THROWS_MATCHES() macro for that purpose.

Here is an example of how I use it with the Equals matcher:

    CATCH_REQUIRE_THROWS_MATCHES(
                  std::make_shared<advgetopt::getopt>(
                            options_environment
                          , sub_argc
                          , sub_argv)
                , advgetopt::getopt_exception_logic
                , Catch::Matchers::Equals(
                          "section \"invalid::name\" includes a section separator (::) in \""
                        + options_filename
                        + "\". We only support one level."));

Only that does not compile unless I have a cast operator in my exceptions. In this case, that's easy enough since I have my own exception. But I'm wondering why the author of Catch2 thought of using a cast to std::string instead of using the what() function.

Here is my current base class exception definition:

class logic_exception_t
    : public std::logic_error
    , public exception_base_t
{
public:
    explicit                    logic_exception_t( std::string const & what, int const stack_trace_depth = STACK_TRACE_DEPTH );
    explicit                    logic_exception_t( char const *        what, int const stack_trace_depth = STACK_TRACE_DEPTH );

    virtual                     ~logic_exception_t() override {}

    virtual char const *        what() const throw() override;
                                operator std::string () const;
};

Here is the operator std::string () const function:

logic_exception_t::operator std::string () const
{
    return what();
}

Is there another way to satisfy the Catch2 requirement and allow for a transformation of an exception to an std::string without having to create a cast operator? I just don't like having a cast which could cause other problems down the road.

Note: I tried to make the cast explicit and Catch2 doesn't like it either. It just passes the exception to a function which expects an std::string.

1 Answers

0
Alexis Wilke On

You can actually define your own watcher, so I decided to write a watcher that would take an exception for its match() function. This works without the casting to std::string!

namespace Catch
{
namespace Matchers
{


class ExceptionWatcher
    : public MatcherBase<std::exception>
{
public:
    ExceptionWatcher(std::string const & expected_message)
        : m_expected_message(expected_message)
    {
    }

    /** \brief Check whether we got a match.
     *
     * This function compares the expected string with the actual exception
     * what() output.
     */
    bool match(std::exception const & e) const override
    {
        return e.what() == m_expected_message;
    }

    /** \brief Describe this matcher.
     *
     * This function produces a string describing what this matcher does.
     *
     * \return The description of this matcher.
     */
    virtual std::string describe() const override
    {
        return "compare the exception what() message with \""
             + m_expected_message
             + "\".";
    }

private:
    std::string     m_expected_message = std::string();
};


inline ExceptionWatcher ExceptionMessage(std::string const & expeted_message)
{
    return ExceptionWatcher(expeted_message);
}



}
// Matchers namespace
}
// Catch namespace