it-swarm.com.de

Testen Sie, ob ein bestimmter Ausnahmetyp ausgelöst wird UND die Ausnahme die richtigen Eigenschaften hat

Ich möchte testen, dass MyException in einem bestimmten Fall geworfen wird. EXPECT_THROW ist hier gut. Ich möchte aber auch prüfen, ob die Ausnahme einen bestimmten Status hat, z. B. e.msg() == "Cucumber overflow".

Wie lässt sich dies am besten in GTest implementieren?

39
Mr. Boy

Ich habe Lilshiestes Antwort meistens an zweiter Stelle, würde aber hinzufügen, dass Sie auch __ überprüfen sollten, dass der falsche Ausnahmetyp nicht ausgelöst wird:

#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();
}
38
Mike Kinghan

Ein Kollege hat die Lösung gefunden, indem er die Ausnahme erneut ausgelöst hat.

Der Knack: keine zusätzlichen FAIL () - Anweisungen, nur die beiden EXPECT ... -Aufrufe, mit denen die tatsächlich gewünschten Bits getestet werden: die Ausnahme als solche und ihr Wert.

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 );
}
26
minastaros

Jeff Langr beschreibt in seinem Buch einen guten Ansatz: Moderne C++ - Programmierung mit testgetriebener Entwicklung :

Wenn Ihr [testing] -Framework keine einzeilige deklarative Zusicherung unterstützt, die sicherstellt, dass eine Ausnahme ausgelöst wird, können Sie die folgende Struktur in Ihrem Test verwenden:

    TEST(ATweet, RequiresUserNameToStartWithAnAtSign) {
        string invalidUser("[email protected]");
        try {
            Tweet tweet("msg", invalidUser);
            FAIL();
        }
        catch(const InvalidUserException& expected) {}
    }

[...] Möglicherweise müssen Sie auch die try-catch-Struktur verwenden, wenn Sie nach dem Auslösen der Ausnahme Nachbedingungen überprüfen müssen. Beispielsweise möchten Sie möglicherweise den Text überprüfen, der dem ausgelösten Ausnahmeobjekt zugeordnet ist.

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

(S.95)

Dies ist der Ansatz, den ich verwendet habe und an anderer Stelle in der Praxis gesehen habe.

Edit: Wie von @MikeKinghan darauf hingewiesen, stimmt ziemlich nicht mit der Funktionalität von EXPECT_THROW überein; Der Test schlägt nicht fehl, wenn die falsche Ausnahme ausgelöst wird. Eine zusätzliche catch-Klausel könnte hinzugefügt werden, um dies zu beheben:

catch(...) {
    FAIL();
}
14
Lilshieste

Ich empfehle, ein neues Makro zu definieren, das auf dem Ansatz von Mike Kinghan basiert.

#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  \
           << "'!";                                                   \
}

Mikes TEST(foo_test,out_of_range) Beispiel wäre dann

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

was meiner Meinung nach am Ende viel lesbarer ist.

1
redwizard792

Da ich mehrere solcher Tests durchführen muss, habe ich ein Makro geschrieben, das im Wesentlichen die Antwort von Mike Kinghan enthält, den gesamten Code der Speicherplatte jedoch "entfernt":

#define ASSERT_THROW_KEEP_AS_E(statement, expected_exception) \
    std::exception_ptr _exceptionPtr; \
    try \
    { \
        (statement);\
        FAIL() << "Expected: " #statement " throws an exception of type " \
          #expected_exception ".\n  Actual: it throws nothing."; \
    } \
    catch (expected_exception const &) \
    { \
        _exceptionPtr = std::current_exception(); \
    } \
    catch (...) \
    { \
        FAIL() << "Expected: " #statement " throws an exception of type " \
          #expected_exception ".\n  Actual: it throws a different type."; \
    } \
    try \
    { \
        std::rethrow_exception(_exceptionPtr); \
    } \
    catch (expected_exception const & e)

Verwendungszweck:

ASSERT_THROW_KEEP_AS_E(foo(), MyException)
{
    ASSERT_STREQ("Cucumber overflow", e.msg());
}

Vorsichtsmaßnahmen:

  • Da das Makro eine Variable im aktuellen Bereich definiert, kann sie nur einmal verwendet werden.
  • C++ 11 wird für std::exception_ptr benötigt
1

Ich verwende das Makro von Matthäus Brandl mit der folgenden geringfügigen Änderung:

Setze die Linie

std::exception_ptr _exceptionPtr;

außerhalb (zB vorher) die Makrodefinition als

static std::exception_ptr _exceptionPtr;

um eine mehrfache Definition des Symbols _exceptionPtr zu vermeiden.

0
morini

Ein Makro erweitert die vorherigen Antworten und überprüft, ob eine Ausnahme eines bestimmten Typs ausgelöst wurde und deren Nachricht mit der angegebenen Zeichenfolge beginnt. 

Der Test schlägt fehl, wenn keine Ausnahme ausgelöst wird, der Ausnahmetyp falsch ist oder wenn die Nachricht nicht mit der angegebenen Zeichenfolge beginnt.

#define ASSERT_THROWS_STARTS_WITH(expr, exc, msg) \
    try\
    {\
            (expr);\
            FAIL() << "Exception not thrown";\
    }\
    catch (const exc& ex)\
    {\
            EXPECT_THAT(ex.what(), StartsWith(std::string(msg)));\
    }\
    catch(...)\
    {\
            FAIL() << "Unexpected exception";\
    } 

Anwendungsbeispiel:

ASSERT_THROWS_STARTS_WITH(foo(-2), std::invalid_argument, "Bad argument: -2");
0
Dave

Ich mag die meisten Antworten. Da es scheint, dass GoogleTest EXPECT_PRED_FORMAT bereitstellt, das dies erleichtert, möchte ich diese Option zur Liste der Antworten hinzufügen:

MyExceptionCreatingClass testObject; // implements TriggerMyException()

EXPECT_PRED_FORMAT2(ExceptionChecker, testObject, "My_Expected_Exception_Text");

wobei ExceptionChecker definiert ist als:

testing::AssertionResult ExceptionChecker(const char* aExpr1,
                                          const char* aExpr2,
                                          MyExceptionCreatingClass& aExceptionCreatingObject,
                                          const char* aExceptionText)
{
  try
  {
    aExceptionCreatingObject.TriggerMyException();
    // we should not get here since we expect an exception
    return testing::AssertionFailure() << "Exception '" << aExceptionText << "' is not thrown.";
  }
  catch (const MyExpectedExceptionType& e)
  {
    // expected this, but verify the exception contains the correct text
    if (strstr(e.what(), aExceptionText) == static_cast<const char*>(NULL))
    {
      return testing::AssertionFailure()
          << "Exception message is incorrect. Expected it to contain '"
          << aExceptionText << "', whereas the text is '" << e.what() << "'.\n";
    }
  }
  catch ( ... )
  {
    // we got an exception alright, but the wrong one...
    return testing::AssertionFailure() << "Exception '" << aExceptionText
    << "' not thrown with expected type 'MyExpectedExceptionType'.";
  }
  return testing::AssertionSuccess();
}
0
EdS