2008-08-19 105 views
9

在我的大多数C++项目中,我频繁使用的断言声明如下:测试用例VS ASSERTION声明

int doWonderfulThings(const int* fantasticData) 
{ 
    ASSERT(fantasticData); 
    if(!fantasticData) 
     return -1; 
    // ,,, 
    return WOW_VALUE; 
} 

但TDD社会似乎喜欢喜欢做这样的事情:

int doMoreWonderfulThings(const int* fantasticData) 
{ 
    if(!fantasticData) 
     return ERROR_VALUE; 
    // ... 
    return AHA_VALUE; 
} 

TEST(TDD_Enjoy) 
{ 
    ASSERT_EQ(ERROR_VALUE, doMoreWonderfulThings(0L)); 
    ASSERT_EQ(AHA_VALUE, doMoreWonderfulThings("Foo")); 
} 

只是我体验第一种方法让我消除了这么多微妙的错误。但TDD方法是处理遗留代码的非常聪明的想法。

“Google” - 他们将“第一种方法”与“用救生背心游走海岸,没有任何安全警卫游泳海洋”相比较。

哪一个更好? 哪一个使软件健壮?

回答

0

我不知道你指的是哪个特定的TDD子社区,但我遇到的TDD模式要么使用Assert.AreEqual()来获得肯定的结果,要么使用ExpectedException机制(例如,.NET中的属性)宣布应该遵守的错误。

1

没有理由说明你的测试包不能捕获断言,比如doMoreWonderfulThings中的断言。这可以通过使ASSERT处理程序支持回调机制来完成,或者您的测试断言包含try/catch块。

4

在我的(有限)体验中,第一个选项比较安全。在一个测试案例中,您只测试预定义的输入并比较结果,只要检查了每个可能的边缘案例,就可以很好地工作。第一个选项只是检查每个输入,从而测试“实时”值,它可以快速过滤出错误,但它会带来性能损失。

Code Complete Steve McConnell得知我们可以成功使用第一种方法来过滤掉debug构建中的错误。在发布版本中,您可以过滤掉所有断言(例如使用编译器标志)以获得额外的性能。

在我看来,最好的办法就是使用这两种方法:

方法1赶上非法值

int doWonderfulThings(const int* fantasticData) 
{ 
    ASSERT(fantasticData); 
    ASSERTNOTEQUAL(0, fantasticData) 

    return WOW_VALUE/fantasticData; 
} 

和方法2进行运算的测试边缘案件。

int doMoreWonderfulThings(const int fantasticNumber) 
{ 
    int count = 100; 
    for(int i = 0; i < fantasticNumber; ++i) { 
     count += 10 * fantasticNumber; 
    } 
    return count; 
} 

TEST(TDD_Enjoy) 
{ 
    // Test lower edge 
    ASSERT_EQ(0, doMoreWonderfulThings(-1)); 
    ASSERT_EQ(0, doMoreWonderfulThings(0)); 
    ASSERT_EQ(110, doMoreWonderfulThings(1)); 

    //Test some random values 
    ASSERT_EQ(350, doMoreWonderfulThings(5)); 
    ASSERT_EQ(2350, doMoreWonderfulThings(15)); 
    ASSERT_EQ(225100, doMoreWonderfulThings(150)); 
} 
2

两种机制都有价值。任何体面的测试框架无论如何都会捕获标准的assert(),所以导致assert失败的测试运行会导致失败的测试。

我通常在每个C++方法的开头都有一系列断言,并带有注释'// preconditions';这只是对我期望该对象在调用该方法时所处的状态的完整性检查。这些与任何TDD框架很好地吻合,因为它们不仅在运行时在测试功能时工作,而且在测试时也能工作。

0

在C++中,当使用大多数测试框架时,我更喜欢方法2。它通常使得更容易理解故障报告。如果在测试后的几个月到几年内进行测试,这是非常宝贵的。

我的理由是大多数C++测试框架都会在没有任何类型的堆栈跟踪信息的情况下打印断言所在的文件和行号。所以大多数情况下,您会在函数或方法中获取报告行号,而不是在测试用例中。

即使断言从调用者被捕获并重新声明,报告行将与catch语句一起使用,并且可能不会接近调用所声明的方法或函数的测试用例行的任何地方。当测试用例中声明的函数可能被多次使用时,这可能非常烦人。

虽然有例外。例如,Google的测试框架有一个作用域跟踪语句,如果发生异常,它将作为跟踪的一部分进行打印。因此,您可以使用跟踪范围将调用包装为广义测试函数,并且可以轻松地在一行或两行中告诉确切测试用例中的哪一行失败。