使用C ++ catch框架验证断言语句

Zac*_*ack 5 c++ assert catch-unit-test

是否可以使用C ++ CATCH框架来验证assert语句正确标识了无效的前提条件?

// 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?
}
Run Code Online (Sandbox Code Playgroud)

JBR*_*son 3

假设示例的第一部分是正在测试的源代码,第二部分是单元测试,那么您需要选择如何处理此问题:

一些开源框架(例如BDEBoost)有自己的 ASSERT 宏,可以在应用程序启动时对其进行配置,使其行为与 C 断言不同。例如,您可以指定失败的 ASSERT 引发异常 - 然后您可以使用 Catch 的REQUIRE_THROWS()断言来验证您的代码是否强制执行它的非 NULL FILE 描述符约定。

溴二苯醚示例

#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);
}
Run Code Online (Sandbox Code Playgroud)

升压示例

#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?
}
Run Code Online (Sandbox Code Playgroud)

您可以推出自己的assert() 宏。这是重新发明轮子 - 请参阅上面的示例。

您可以更改代码以抛出std::invalid_argument()异常:

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

您可以测试您的代码是否强制执行其合同:

REQUIRE_THROWS_AS(loadDataFile(NULL), std::invalid_argument);
Run Code Online (Sandbox Code Playgroud)

这将异常(以及处理它们的需要)引入到您的代码中,这是一个比您的客户可能满意的更大的变化 - 一些公司有无异常规则,一些平台(例如嵌入式)不支持异常。

最后,如果您确实愿意,您可以更改代码的接口以暴露合约失败:

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;
}
Run Code Online (Sandbox Code Playgroud)

...但这有一个固有的风险,即客户端不检查返回值,这是许多错误的原因,并且感觉就像是 C 语言一样。