2008-11-06 68 views
15

我使用Boost测试框架来单元测试我的C++代码,并想知道是否可以测试一个函数是否可以断言?是的,听起来有点奇怪,但忍耐着我!我的许多函数在输入时检查输入参数,断言它们是否无效,并且对此进行测试会很有用。例如:在Boost测试框架中断言的测试

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

我知道这是一个老话题,但我说我想出了在工作顺手拿一个解决方案。 – grokus 2011-07-26 22:26:19

回答

5

我不这么认为。你总是可以编写你自己的assert,它引发一个异常,然后使用BOOST_CHECK_NOTHROW()来处理这个异常。

0

对不起,但你是以错误的方式攻击你的问题。

“断言”是魔鬼(a.k.a.“C”)的产卵,对任何有适当例外的语言都是无用的。 waaaaaay更好的是重新实现一个类似assert的功能和异常。这样,您实际上有机会以正确的方式处理错误(包括正确的清理过程)或随意触发它们(用于单元测试)。另外,如果你的代码在Windows中运行,当你断言失败时,你会得到一个无用的弹出窗口,提供你调试/放弃/重试。很好的自动化单元测试。

所以,你自己一个忙,重新编写一个抛出异常的断言函数。这里有一个: How can I assert() without using abort()?

将它包装在一个宏中,以便获得_ _FILE _和_ _ LINE _ _(对调试有用),然后就完成了。

+12

如果你认为这是你使用错误的方式。断言只能用于必须为真或者是*程序员*错误的东西,请参阅问题中给出的示例。如果程序员以错误的方式使用某个函数,您希望尽快知道该函数。 – 2008-11-07 12:44:17

+1

我同意安德烈亚斯。断言和可能的情况下static_asserts不应该被例外替代。它们旨在检测程序员的错误。 – 2008-11-07 20:08:24

9

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

无论怎样,不变量都是应该始终如一的事情。对于那些,我使用断言。像你这样的事情不应该给我一个零指针给你给我的输出缓冲区。这是代码中的一个错误,简单明了。在调试版本中,它会断言并给我一个修正它的机会。在零售版本中,它会导致访问冲突,并生成一个小型转储(Windows,至少在我的代码中)或coredump(Mac/unix)。没有catch,我可以做,这有意义处理解引用零指针。在Windows catch (...)可以抑制访问违规行为,并给用户一种错误的自信感,即当事情已经发生可怕,可怕的错误时,事情就会好转。

这就是为什么我开始相信,catch (...)一般是在C代码味道++和在那里我可以认为存在的唯一合理的地方是在main(或WinMain)你生成一个核心权利之前转储并礼貌地退出该应用程序。

运行时错误类似于“由于权限而无法写入此文件”或“由于磁盘已满而无法写入此文件”。对于这些类型的错误抛出异常是有道理的,因为用户可以做一些事情,比如更改目录权限,删除一些文件或选择其他位置来保存文件。这些运行时错误可由用户更正。用户不能纠正违反常量的情况,只能由程序员纠正。 (有时两者是相同的,但通常不是。)

您的单元测试应强制代码抛出代码可能生成的运行时错误异常。您可能还想强制协作者的异常情况,以确保您的系统处于异常安全状态。

但是,我不相信在试图强制你的代码对单元测试的不变量进行断言时是有价值的。

3

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

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

至于意图和机制,应该指出的是,异常没有什么魔力。前段时间,Peter Dimov在Boost邮件列表(大约)上说,“例外只是非本地跳转机制”。这是非常真实的。如果您的应用程序可能会在发生内部错误后继续执行,而没有在修复之前损坏某些内容的风险,则可以实施导致C++异常的自定义断言。但它不会改变意图,也不会使断言测试更合理。

12

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

Boost UTF使用boost::execution_monitor(在 <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的目的)。

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

  • 它可能会打破未来的升压版本。

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

  • 如果在发布配置为msvc,一些断言或其他致命错误 反正会发生它不会被捕获。 (请参阅execution_monitor文档)。

  • 如果你使用assert或不是由你决定。我喜欢他们。

参见:

此外,由于Gennadiy Rozental(升压试验的作者),如果你碰巧 阅读,伟大的工作!

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 

我最初的动机是为了帮助调试,即断言()可以快速调试和异常往往是很难在gdb调试。我的编译标志似乎很好地平衡了可调试性和可测试性。

希望这有助于