2008-08-19 3 views
9

В моем основном проекте я активно используется Assertion заявление C++ следующим образом:Test Cases 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 узнает, что первый метод может быть успешно использован для фильтрации ошибок в отладке. В выпуске вы можете отфильтровывать все утверждения (например, с флагом компилятора), чтобы получить дополнительную производительность.

На мой взгляд, лучший способ заключается в использовании обоих методов:

Метод 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 имеет ограниченный оператор трассировки, который будет печататься как часть трассы, если возникает исключение. Таким образом, вы можете обернуть вызов обобщенной тестовой функции с помощью области трассировки и легко указать, в строке или двух, какая строка в точной тестовой версии завершилась неудачно.