Use C++ catch framework to verify assert statement

与世无争的帅哥 提交于 2019-12-10 13:57:00

问题


Is it possible to use the C++ CATCH framework to verify that an assert statement correctly identifies an invalid precondition?

// Source code
void loadDataFile(FILE* input) {
  assert(input != NULL);
  ...
}

// Test code
TEST_CASE("loadDataFile asserts out when passed NULL", "[loadDataFile]") {
   loadDataFile(NULL)
   // Now what do I look for?
}

回答1:


Assuming that the first section of your example is the source code under test and the second part is the unit test, then you'll need to make a choice in how you handle this:

Some open-source frameworks like BDE and Boost have their own ASSERT macro which can be configured at application startup to behave different than C assert. For example, you can specify that a failed ASSERT throws an exception - and then you could use Catch's REQUIRE_THROWS() assertion to verify that your code enforced it's non-NULL FILE descriptor contract.

BDE example

#include <bsls_assert.h>

void loadDataFile(FILE* input) {
  BSLS_ASSERT_OPT(input != NULL);
  ...
}

TEST_CASE("loadDataFile asserts out when passed NULL", "[loadDataFile]") {
   // Opt-in to the 'throw exception on assert failure' handler
   // just for this test case.
   bsls::AssertFailureHandlerGuard guard(&bsls::Assert::failThrow);
   REQUIRE_THROWS_AS(loadDataFile(NULL), bsls::AssertFailedException);
}

Boost example

#include <boost/assert.hpp>

void loadDataFile(FILE* input) {
  BOOST_ASSERT(input != NULL);
  ...
}

namespace boost {
void assertion_failed(char const * expr, char const * function, char const * file, long line) {
    throw std::runtime_error("Assertion Failed"); // TODO: use expr, function, file, line
}
}

TEST_CASE("loadDataFile asserts out when passed NULL", "[loadDataFile]") {
   REQUIRE_THROWS(loadDataFile(NULL));
   // Now what do I look for?
}

You could roll your own assert() macro. This is re-inventing the wheel - see above examples instead.

You could change your code to throw std::invalid_argument() exception instead:

  void loadDataFile(FILE* input) {
    if (input == NULL) {
      throw std::invalid_argument("input file descriptor cannot be NULL");
    }
    ...
  }

You can test that your code enforces it's contract with:

REQUIRE_THROWS_AS(loadDataFile(NULL), std::invalid_argument);

This is introducing exceptions (and the need to handle them) into your code and this is a bigger change than you clients may be happy with - some companies have a no-exceptions rule, some platforms (e.g. embedded) do not support exceptions.

Finally, if you really wanted to, you could alter your code's interface to expose contract failure:

enum LoadDataFile_Result {
  LDF_Success,
  LDF_InputIsNull,
  ...
};

LoadDataFile_Result loadDataFile(FILE* input) {
  if (input == NULL) {
    // bail out early for contract failure
    return LDF_InputIsNull;
  }
  // input is non-NULL
  ...
  return LDF_Success;
}

...but this has the inherent risk that clients don't check the return value, the cause of many bugs, and feels like C all over again.




回答2:


You may be interested in the google test framework. It has the ability to catch abnormal program termination with:

ASSERT_DEATH(statement, regex);
ASSERT_DEATH_IF_SUPPORTED(statement, regex);
ASSERT_EXIT(statement, predicate, regex);

EXPECT_DEATH(statement, regex);
EXPECT_DEATH_IF_SUPPORTED(statement, regex);
EXPECT_EXIT(statement, predicate, regex);

regex matches text on stderr predicate matches the program's exit code.

I suspect this works by forking the test program before the assertion.

documentation here:

https://github.com/google/googletest/blob/master/googletest/docs/advanced.md



来源:https://stackoverflow.com/questions/38533272/use-c-catch-framework-to-verify-assert-statement

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!