Test a specific exception type is thrown AND the exception has the right properties

前端 未结 10 1636
梦如初夏
梦如初夏 2020-12-24 01:55

I want to test that MyException is thrown in a certain case. EXPECT_THROW is good here. But I also want to check the exception has a specific state

相关标签:
10条回答
  • 2020-12-24 02:20

    I recommend defining a new macro based on Mike Kinghan's approach.

    #define ASSERT_EXCEPTION( TRY_BLOCK, EXCEPTION_TYPE, MESSAGE )        \
    try                                                                   \
    {                                                                     \
        TRY_BLOCK                                                         \
        FAIL() << "exception '" << MESSAGE << "' not thrown at all!";     \
    }                                                                     \
    catch( const EXCEPTION_TYPE& e )                                      \
    {                                                                     \
        EXPECT_EQ( MESSAGE, e.what() )                                    \
            << " exception message is incorrect. Expected the following " \
               "message:\n\n"                                             \
            << MESSAGE << "\n";                                           \
    }                                                                     \
    catch( ... )                                                          \
    {                                                                     \
        FAIL() << "exception '" << MESSAGE                                \
               << "' not thrown with expected type '" << #EXCEPTION_TYPE  \
               << "'!";                                                   \
    }
    

    Mike's TEST(foo_test,out_of_range) example would then be

    TEST(foo_test,out_of_range)
    {
        foo f;
        ASSERT_EXCEPTION( { f.bar(111); }, std::out_of_range, "Out of range" );
    }
    

    which I think ends up being much more readable.

    0 讨论(0)
  • 2020-12-24 02:21

    A colleague came up with the solution by just re-throwing the exception.

    The knack: no need of extra FAIL() statements, just the two EXPECT... calls that test the bits you actually want: the exception as such and its value.

    TEST(Exception, HasCertainMessage )
    {
        // this tests _that_ the expected exception is thrown
        EXPECT_THROW({
            try
            {
                thisShallThrow();
            }
            catch( const MyException& e )
            {
                // and this tests that it has the correct message
                EXPECT_STREQ( "Cucumber overflow", e.what() );
                throw;
            }
        }, MyException );
    }
    
    0 讨论(0)
  • 2020-12-24 02:25

    You can try Boost lightweight test:

    #include <boost/detail/lightweight_test.hpp>
    #include <stdexcept>
    
    void function_that_would_throw(int x)
    {
      if (x > 0) {
        throw std::runtime_error("throw!");
      }
    }
    
    int main() {
     BOOST_TEST_THROWS(function_that_would_throw(10), std::runtime_error);
     boost::report_errors();
    }
    
    0 讨论(0)
  • 2020-12-24 02:26

    Jeff Langr describes a good approach in his book, Modern C++ Programming with Test-Driven Development:

    If your [testing] framework does not support a single-line declarative assert that ensures an exception is thrown, you can use the following structure in your test:

        TEST(ATweet, RequiresUserNameToStartWithAnAtSign) {
            string invalidUser("notStartingWith@");
            try {
                Tweet tweet("msg", invalidUser);
                FAIL();
            }
            catch(const InvalidUserException& expected) {}
        }
    

    [...] You might also need to use the try-catch structure if you must verify any postconditions after the exception is thrown. For example, you may want to verify the text associated with the thrown exception object.

        TEST(ATweet, RequiresUserNameToStartWithAtSign) {
            string invalidUser("notStartingWith@");
            try {
                Tweet tweet("msg", invalidUser);
                FAIL();
            }
            catch(const InvalidUserException& expected) {
                ASSERT_STREQ("notStartingWith@", expected.what());
            }
        }
    

    (p.95)

    This is the approach I've used, and have seen in practice elsewhere.

    Edit: As has been pointed out by @MikeKinghan, this doesn't quite match the functionality provided by EXPECT_THROW; the test doesn't fail if the wrong exception is thrown. An additional catch clause could be added to address this:

    catch(...) {
        FAIL();
    }
    
    0 讨论(0)
  • 2020-12-24 02:32

    I mostly second Lilshieste's answer but would add that you also should verify that the wrong exception type is not thrown:

    #include <stdexcept>
    #include "gtest/gtest.h"
    
    struct foo
    {
        int bar(int i) {
            if (i > 100) {
                throw std::out_of_range("Out of range");
            }
            return i;
        }
    };
    
    TEST(foo_test,out_of_range)
    {
        foo f;
        try {
            f.bar(111);
            FAIL() << "Expected std::out_of_range";
        }
        catch(std::out_of_range const & err) {
            EXPECT_EQ(err.what(),std::string("Out of range"));
        }
        catch(...) {
            FAIL() << "Expected std::out_of_range";
        }
    }
    
    int main(int argc, char **argv) {
      ::testing::InitGoogleTest(&argc, argv);
      return RUN_ALL_TESTS();
    }
    
    0 讨论(0)
  • 2020-12-24 02:32

    My version; it produces the same output as EXPECT_THROW and just adds the string test:

    #define EXPECT_THROW_MSG(statement, expected_exception, expected_what)                    \
      try                                                                                     \
      {                                                                                       \
        statement;                                                                            \
        FAIL() << "Expected: " #statement " throws an exception of type " #expected_exception \
                  ".\n"                                                                       \
                  "  Actual: it throws nothing.";                                             \
      }                                                                                       \
      catch (const expected_exception& e)                                                     \
      {                                                                                       \
        EXPECT_EQ(expected_what, std::string{e.what()});                                      \
      }                                                                                       \
      catch (...)                                                                             \
      {                                                                                       \
        FAIL() << "Expected: " #statement " throws an exception of type " #expected_exception \
                  ".\n"                                                                       \
                  "  Actual: it throws a different type.";                                    \
      }
    
    0 讨论(0)
提交回复
热议问题