【问题标题】:Test Cases VS ASSERTION statement测试用例 VS ASSERTION 语句
【发布时间】:2010-09-06 05:47:11
【问题描述】:

在我最常使用的 C++ 项目中,我大量使用了 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”——他们将“FIRST METHOD”比作“穿着救生衣走在岸边,在没有任何安全防护的情况下畅游大海”。

哪个更好? 哪一个让软件变得健壮?

【问题讨论】:

    标签: c++ tdd defensive-programming


    【解决方案1】:

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

    我的原因是大多数 C++ 测试框架会打印出断言发生的文件和行号,而没有任何堆栈跟踪信息。所以大多数时候你会在函数或方法内部而不是在测试用例内部获得报告行号。

    即使断言被调用者捕获并重新断言,报告行也将使用 catch 语句,并且可能不会靠近调用断言的方法或函数的测试用例行。当断言的函数可能在测试用例中被多次使用时,这真的很烦人。

    但也有例外。例如,Google 的测试框架有一个范围跟踪语句,如果发生异常,它将作为跟踪的一部分打印出来。因此,您可以使用跟踪范围包装对通用测试函数的调用,并在一两行内轻松判断确切测试用例中的哪一行失败。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

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

        我认为最好的方法是同时使用这两种方法:

        捕捉非法值的方法一

        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));
        }
        

        【讨论】:

          【解决方案4】:

          您的测试包没有理由无法捕获诸如 doMoreWonderfulThings 中的断言。这可以通过让您的 ASSERT 处理程序支持回调机制来完成,或者您的测试断言包含一个 try/catch 块。

          【讨论】:

            【解决方案5】:

            我不知道您指的是哪个特定的 TDD 子社区,但我遇到的 TDD 模式要么使用 Assert.AreEqual() 来获得积极的结果,要么使用 ExpectedException 机制(例如,.NET 中的属性)声明应该观察到的错误。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-09-13
              • 2021-11-06
              • 2018-10-31
              • 1970-01-01
              相关资源
              最近更新 更多