我使用Boost Test框架对我的C ++代码进行单元测试,并想知道是否可以测试函数是否会断言?是的,听起来有点奇怪,但请忍受我!我的许多函数在输入时检查输入参数,断言它们是否无效,并且测试它是有用的。例如:
void MyFunction(int param)
{
assert(param > 0); // param cannot be less than 1
...
}
我希望能够做到这样的事情:
BOOST_CHECK_ASSERT(MyFunction(0), true);
BOOST_CHECK_ASSERT(MyFunction(-1), true);
BOOST_CHECK_ASSERT(MyFunction(1), false);
...
你可以检查使用Boost测试抛出的异常,所以我想知道是否还有一些断言魔法......
答案 0 :(得分:13)
遇到同样的问题,我挖掘了文档(和代码)和 找到了“解决方案”。
Boost UTF使用boost::execution_monitor
(in
<boost/test/execution_monitor.hpp>
)。这是为了抓住目标而设计的
测试执行期间可能发生的一切。找到断言时
execution_monitor拦截它并抛出boost::execution_exception
。从而,
通过使用BOOST_REQUIRE_THROW
,您可以断言断言的失败。
这样:
#include <boost/test/unit_test.hpp>
#include <boost/test/execution_monitor.hpp> // for execution_exception
BOOST_AUTO_TEST_CASE(case_1)
{
BOOST_REQUIRE_THROW(function_w_failing_assert(),
boost::execution_exception);
}
应该做的伎俩。 (这对我有用。)
然而(或免责声明):
这对我有用。也就是说,在Windows XP,MSVC 7.1上,提升1.41.0。它可能 在您的设置上不合适或不合适。
这可能不是Boost Test的作者的意图。 (虽然它似乎是execution_monitor的目的)。
它会以同样的方式处理每种形式的致命错误。我可以 除断言之外的其他东西都失败了。在这种情况下你 可能会错过内存损坏错误,和/或错过失败的失败断言。
未来的升级版本可能会中断。
我预计如果在Release配置中运行它会失败,因为断言将是 禁用以及断言设置为阻止的代码 跑。导致非常不确定的行为。
如果在msvc的Release配置中,有一些类似断言或其他致命错误 无论如何它都会发生它不会被抓住。 (参见execution_monitor docs)。
如果您使用assert与否取决于您。我喜欢他们。
请参阅:
execution-monitor用户指南。
另外,感谢Gennadiy Rozental(Boost测试的作者),如果你碰巧 读这个,伟大的工作!!
答案 1 :(得分:9)
我想检查两种错误:不变量和运行时错误。
无论什么,不变量都应该是真实的。对于那些,我使用断言。像你这样的事情不应该传递给你我输出缓冲区的零指针。这是代码中的一个错误,简单明了。在调试版本中,它会断言并给我一个纠正它的机会。在零售版本中,它将导致访问冲突并生成小型转储(Windows,至少在我的代码中)或coredump(Mac / unix)。我可以做的没有catch
来处理取消引用零指针是有意义的。在Windows上catch (...)
可以抑制访问冲突,并且当用户已经非常可怕,可怕的错误时,会让用户产生一种错误的信心,即事情是正常的。
这就是为什么我开始相信catch (...)
通常是C ++中的代码气味的一个原因,也是我认为存在的唯一合理的地方是main
(或{ {1}})在您生成核心转储并礼貌地退出应用程序之前。
运行时错误类似于“由于权限我无法写入此文件”或“由于磁盘已满而无法写入此文件”。对于这些类型的错误,抛出异常是有道理的,因为用户可以对其执行某些操作,例如更改目录的权限,删除某些文件或选择备用位置来保存文件。这些运行时错误可由用户纠正。用户无法仅通过程序员纠正违反不变量的情况。 (有时两者是相同的,但通常不是。)
您的单元测试应强制代码抛出代码可能生成的运行时错误异常。您可能还希望强制协作者的异常,以确保您的被测系统是异常安全的。
但是,我不相信尝试强制您的代码使用单元测试对不变量进行断言是有价值的。
答案 2 :(得分:6)
我不这么认为。您总是可以编写自己的断言,它会抛出异常,然后对该异常使用BOOST_CHECK_NOTHROW()。
答案 3 :(得分:3)
我认为这个问题和一些回复会将运行时错误检测与错误检测混淆。他们也混淆了意图和机制。
运行时错误可能发生在100%正确的程序中。它需要检测,需要正确的报告和处理,并且应该进行测试。错误也会发生,为了方便程序员,最好使用前置条件检查或不变检查或随机断言尽早捕获它们。但这是程序员的工具。对于普通用户来说,错误消息没有任何意义,测试正确编写的程序永远不会传递给它的数据上的函数行为似乎是不合理的。
至于意图和机制,应该注意的是,例外不是什么神奇的事。不久前,Peter Dimov在Boost邮件列表(大约)上说“例外只是非本地跳转机制”。这是非常正确的。如果你有一个应用程序,它可以在一些内部错误后继续,没有在修复之前某些东西会被破坏的风险,你可以实现抛出C ++异常的自定义断言。但它不会改变意图,也不会使断言的测试更合理。
答案 4 :(得分:2)
在工作中我遇到了同样的问题。我的解决方案是使用编译标志。当我的标志GROKUS_TESTABLE打开时,我的GROKUS_ASSERT变成了异常,使用Boost可以测试抛出异常的代码路径。当GROKUS_TESTABLE关闭时,GROKUS_ASSERT被转换为c ++ assert()。
#if GROKUS_TESTABLE
#define GROKUS_ASSERT ... // exception
#define GROKUS_CHECK_THROW BOOST_CHECK_THROW
#else
#define GROKUS_ASSERT ... // assert
#define GROKUS_CHECK_THROW(statement, exception) {} // no-op
#endif
我最初的动机是帮助调试,即assert()可以快速调试,而异常通常在gdb中更难调试。我的编译标志似乎很好地平衡了可调试性和可测试性。
希望这有帮助
答案 5 :(得分:0)
很抱歉,但你的方法是错误的。
“assert”是魔鬼的产物(a.k.a。“C”),对任何有正当例外的语言都没用。使用异常重新实现类似断言的功能更好。这样你实际上有可能以正确的方式处理错误(包括适当的清理程序)或随意触发它们(用于单元测试)。
此外,如果您的代码在Windows中运行,当您断言失败时,您将获得一个无用的弹出窗口,您可以调试/中止/重试。非常适合自动化单元测试。
所以请自己帮个忙并重新编写一个抛出异常的断言函数。这里有一个: How can I assert() without using abort()?
将它包装在一个宏中,这样你就可以获得_ _FILE _ _和_ _ LINE _ _(对调试很有用),你就完成了。