我对测试一些包含来自 assert.h.
的断言宏的函数有一些担忧如果断言失败,测试也会失败。 这给我留下了一些永远无法工作的测试用例。
例如一个函数而不是指示失败(返回 false 或类似的东西)断言。
是否有解决方案(包含断言的单元测试函数)?
也许这只是我,但我认为如果你有断言失败,在你修复它们之前你甚至不应该考虑更高级别的单元测试。这个想法是,如果代码编写得当,断言在任何情况下都应该 never 失败,包括单元测试。或者至少我是这样写代码的。
您可能正在测试断言在您期望的时候(输入错误)中止的事实。
测试框架 Google Test 作为 ASSERT_DEATH 宏,它将测试程序是否在您期望的位置中止(如断言)。
您还可以使用定义的 NDEBUG 进行编译(-DNDEBUG 与 gcc)以禁用单元测试的断言。
不,单元测试是你在开发过程中所做的。断言是一个运行时构造。
根据我的经验,大多数时候断言在生产中被关闭。但是您应该始终进行测试。
CppUnit 是一个很好的测试框架。它是 C++ 的 nUnit 系列的一部分。
断言在任何情况下都不应失败。如果它们在您的测试中失败,则表明存在逻辑错误。基本上,如果您的函数执行“assert(0)”而不是返回错误代码,那么应该重写该函数。如果中止是所需的行为,则 exit() 是合适的,但 assert() 不合适。
如果断言在测试期间失败,则代码有误,必须更改。代码“assert( x )”应该被解释为“程序的逻辑要求 x 为真。在任何情况下它都不能为假。”如果你有一个导致断言失败的单元测试,那么这个语句显然是无效的,必须修改。
基本上听起来您的测试框架不是为了测试您的断言而构建的。
有了一个会停止进程的断言,你需要一些东西来监视你的执行状态。
boost-test 如何做到这一点的例子: http://www.boost.org/doc/libs/1_34_0/libs/test/doc/components/prg_exec_monitor/index.html
我有一段时间没有完成 C 或 C++ 编码,但是,我会从类似的技术开始。
假设您的代码已准备好妥善处理断言关闭时会触发断言的情况,并且您想对这些情况进行单元测试确实处理得当,一种方法是编译带有断言的测试可执行文件在相关目标文件中禁用。
例如,假设你在
Foo.c
中有这个功能:
Foo* bar(int const i)
{
assert(i != 0);
if (i == 0)
{
/*
* NOTE: The code below does NOT assume that `i != 0`
* just because there's an assert up there.
*
* When asserts are disabled, this returns `NULL`.
*/
return NULL;
}
// Do something with `i` here and return a non-`NULL` pointer.
}
你想测试
bar()
在用 NULL
调用时是否真的返回 0
。
在您的 makefile 中,假设您有两个可执行文件:
${MAIN_EXE}
,您的常规可执行文件,您正在编写您的应用程序以及您wantFoo.c
的断言enabled${TESTS_EXE}
,运行单元测试的可执行文件以及您想要Foo.c
的断言disabled从
Foo.c
,您还可以生成两个不同的目标文件:
Foo.o
,带有断言 enabled 的那个,将链接到 ${MAIN_EXE}
Foo_test.o
,带有断言disabled的那个,被链接到${TESTS_EXE}
# Main executable recipes
${MAIN_EXE}: main.o Foo.o
${CC} ${CFLAGS} main.o Foo.o -o ${MAIN_EXE}
Foo.o: Foo.c Foo.h
${CC} ${CFLAGS} -c Foo.c -o Foo.o
# ...
# Unit test executable recipes
${TESTS_EXE}: tests.o Foo_test.o
${CC} ${CFLAGS} tests.o Foo_test.o -o ${TESTS_EXE}
# For the test object, disable `Foo.c`'s asserts with `-DNDEBUG`.
Foo_test.o: Foo.c Foo.h
${CC} ${CFLAGS} -DNDEBUG -c Foo.c -o Foo_test.o
现在,当运行您的测试可执行文件时,只有测试可执行文件的断言将被启用,允许您像这样进行单元测试
bar()
:
Foo* f = NULL;
f = bar(0); // Won't trigger the assert in `Foo.c`!
assert(f == NULL);
f = bar(5);
assert(f != NULL);
运行常规可执行文件时,
Foo.c
的断言仍将正常启用,提醒您代码中的错误。