Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test Cases VS ASSERTION statement

In my most C++ project I heavily used ASSERTION statement as following:

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

But TDD community seems like to enjoy doing something like this:

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

Just with my experiences first approaches let me remove so many subtle bugs. But TDD approaches are very smart idea to handle legacy codes.

"Google" - they compare "FIRST METHOD" to "Walk the shore with life-vest, swim ocean without any safe guard".

Which one is better? Which one makes software robust?

like image 747
popopome Avatar asked Aug 19 '08 23:08

popopome


2 Answers

In my (limited) experience the first option is quite a bit safer. In a test-case you only test predefined input and compare the outcome, this works well as long as every possible edge-case has been checked. The first option just checks every input and thus tests the 'live' values, it filters out bugs real quickly, however it comes with a performance penalty.

In Code Complete Steve McConnell learns us the first method can be used successfully to filter out bugs in a debug build. In release build you can filter-out all assertions (for instance with a compiler flag) to get the extra performance.

In my opinion the best way is to use both methods:

Method 1 to catch illegal values

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

    return WOW_VALUE / fantasticData;
}

and method 2 to test edge-cases of an algorithm.

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));
}
like image 158
Huppie Avatar answered Oct 04 '22 07:10

Huppie


Both mechanisms have value. Any decent test framework will catch the standard assert() anyway, so a test run that causes the assert to fail will result in a failed test.

I typically have a series of asserts at the start of each c++ method with a comment '// preconditions'; it's just a sanity check on the state I expect the object to have when the method is called. These dovetail nicely into any TDD framework because they not only work at runtime when you're testing functionality but they also work at test time.

like image 34
Andrew Avatar answered Oct 04 '22 07:10

Andrew