22

我使用 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 Test 检查抛出的异常,所以我想知道是否也有一些断言魔法......

4

6 回答 6

14

遇到同样的问题,我翻阅了文档(和代码)并找到了一个“解决方案”。

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 上,boost 1.41.0。您的设置可能不合适或损坏。

  • 这可能不是 Boost Test 的作者的本意。(虽然它似乎是 execution_monitor 的目的)。

  • 它将以相同的方式处理每种形式的致命错误。我可能是您的断言以外的其他东西失败了。在这种情况下,您可能会错过 ega 内存损坏错误,和/或错过失败的失败断言。

  • 它可能会在未来的增强版本中中断。

  • 我预计如果在发布配置中运行它会失败,因为断言将被禁用并且断言设置为阻止的代码将运行。导致非常不确定的行为。

  • 如果在 msvc 的发布配置中,无论如何都会发生一些类似断言或其他致命错误,它不会被捕获。(参见 execution_monitor 文档)。

  • 是否使用断言取决于您。我喜欢他们。

看:

另外,感谢 Gennadiy Rozental(Boost Test 的作者),如果你碰巧读到了这篇文章,干得好!!

于 2009-11-20T19:50:50.960 回答
9

我喜欢检查两种错误:不变量和运行时错误。

不变量是无论如何都应该始终为真的事物。对于那些,我使用断言。像你这样的事情不应该给我一个你给我的输出缓冲区的零指针。这是代码中的一个错误,简单明了。在调试版本中,它会断言并给我一个纠正它的机会。在零售版本中,它会导致访问冲突并生成小型转储(Windows,至少在我的代码中)或核心转储(Mac/unix)。对于catch解除对零指针的引用,我无法做到这一点。在 Windowscatch (...)上,可以抑制访问冲突,并给用户一种错误的自信感,当事情已经发生了可怕的、可怕的错误时,事情已经好了。

这就是为什么我开始相信这catch (...)通常是 C++ 中的代码异味的原因之一,并且我能想到的唯一合理的地方是在您生成核心转储并礼貌地退出应用程序之前的main(或)中。WinMain

运行时错误是诸如“我不能写这个文件,因为权限”或“我不能写这个文件,因为磁盘已满”之类的东西。对于这些类型的错误,抛出异常是有意义的,因为用户可以对其执行一些操作,例如更改目录的权限、删除一些文件或选择备用位置来保存文件。这些运行时错误可由用户纠正。用户无法纠正对不变量的违反,只能由程序员纠正。(有时两者是相同的,但通常不是。)

您的单元测试应该强制代码抛出您的代码可能生成的运行时错误异常。您可能还希望从协作者那里强制异常,以确保您的被测系统是异常安全的。

但是,我不认为尝试通过单元测试强制您的代码针对不变量进行断言是没有价值的。

于 2009-07-19T17:01:42.590 回答
7

我不这么认为。您总是可以编写自己的断言来引发异常,然后使用 BOOST_CHECK_NOTHROW() 来处理该异常。

于 2008-11-06T23:05:21.127 回答
3

我认为这个问题以及一些回复将运行时错误检测与错误检测混淆了。他们还混淆了意图和机制。

运行时错误是在 100% 正确的程序中可能发生的事情。它需要检测,需要适当的报告和处理,并且应该进行测试。错误也会发生,为了程序员的方便,最好使用前置条件检查或不变检查或随机断言尽早发现它们。但这是程序员的工具。该错误消息对于普通用户来说毫无意义,并且在正确编写的程序永远不会传递给它的数据上测试函数行为似乎是不合理的。

至于意图和机制,需要注意的是,异常并不是什么魔法。前段时间,Peter Dimov 在 Boost 邮件列表(大约)上说“异常只是非本地跳转机制”。这是非常真实的。如果您的应用程序可以在发生一些内部错误后继续,而不会有在修复之前损坏某些东西的风险,您可以实现引发 C++ 异常的自定义断言。但它不会改变意图,也不会使断言测试更加合理。

于 2009-07-19T19:28:51.280 回答
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 中更难调试。我的编译标志似乎很好地平衡了可调试性和可测试性。

希望这可以帮助

于 2011-07-26T22:24:29.260 回答
0

对不起,但你以错误的方式解决你的问题。

“assert”是魔鬼的产物(又名“C”),对于任何有适当例外的语言都是无用的。重新实现带有异常的类似断言的功能会更好。这样,您实际上就有机会以正确的方式处理错误(包括正确的清理程序)或随意触发它们(用于单元测试)。

此外,如果您的代码曾经在 Windows 中运行,那么当您的断言失败时,您会得到一个无用的弹出窗口,让您可以调试/中止/重试。非常适合自动化单元测试。

所以帮自己一个忙,重新编写一个抛出异常的断言函数。这里有一个: 如何在不使用 abort() 的情况下断言 ()?

将它包装在一个宏中,这样你就可以得到 _ _FILE _ _ 和 _ _ LINE _ _ (对调试有用),你就完成了。

于 2008-11-06T23:24:42.273 回答