Используйте C++ catch Framework для проверки утверждения assert
Можно ли использовать 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?
}
3 ответа
Предполагая, что первая часть вашего примера - это тестируемый исходный код, а вторая часть - модульный тест, вам нужно будет сделать выбор:
Некоторые платформы с открытым исходным кодом, такие как BDE и Boost, имеют свой собственный макрос ASSERT, который можно настроить при запуске приложения так, чтобы он вел себя не так, как C assert. Например, вы можете указать, что сбойный ASSERT генерирует исключение, а затем вы можете использовать утверждение CQU REQUIRE_THROWS(), чтобы убедиться, что ваш код принудительно использует контракт, отличный от NULL FILE-дескриптора.
Пример BDE
#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);
}
Пример повышения
#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?
}
Вы можете свернуть свой собственный макрос assert(). Это заново изобретать колесо - см. Примеры выше.
Вы можете изменить свой код, чтобы вместо этого выдавать исключение std::invalid_argument():
void loadDataFile(FILE* input) {
if (input == NULL) {
throw std::invalid_argument("input file descriptor cannot be NULL");
}
...
}
Вы можете проверить, что ваш код обеспечивает его контракт с:
REQUIRE_THROWS_AS(loadDataFile(NULL), std::invalid_argument);
Это вводит исключения (и необходимость их обработки) в ваш код, и это большее изменение, чем могут вас порадовать клиенты - в некоторых компаниях есть правило без исключений, некоторые платформы (например, встроенные) не поддерживают исключения.
Наконец, если вы действительно этого хотите, вы можете изменить интерфейс вашего кода, чтобы показать ошибку контракта:
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;
}
... но при этом существует риск того, что клиенты не проверяют возвращаемое значение, причину многих ошибок, и снова чувствуют себя как С.
Вы можете быть заинтересованы в Google Test Framework. Он может отлавливать аварийное завершение программы с помощью:
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
соответствует текст на stderr
predicate
соответствует коду выхода программы.
Я подозреваю, что это работает, разлагая тестовую программу перед утверждением.
документация здесь:
https://github.com/google/googletest/blob/master/googletest/docs/advanced.md
Если вы можете имитировать функции C, вы можете временно изменить ошибку утверждения на исключение.
Например, при использовании Hippomocks ваш тестовый пример будет выглядеть примерно так:
#ifndef NDEBUG
TEST_CASE("loadDataFile asserts out when passed NULL", "[loadDataFile]") {
MockRepository mocks;
mocks.OnCallFunc(__assert_fail).Throw(nullptr);
CHECK_THROWS_AS(loadDataFile(NULL), std::nullptr_t);
}
#endif
Переносимость может быть проблемой, я тестировал это только в Linux с glibc и musl.