Тестовые случаи против утверждения ASSERTION - PullRequest
9 голосов
/ 20 августа 2008

В моем большинстве проектов на 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 - очень умная идея для обработки устаревших кодов.

«Гугл» - они сравнивают «ПЕРВЫЙ МЕТОД» с «Иди на берег со спасательным жилетом, плавай в океане без всякой безопасной охраны».

Какой из них лучше? Какой из них делает программное обеспечение надежным?

Ответы [ 5 ]

4 голосов
/ 20 августа 2008

По моему (ограниченному) опыту первый вариант немного безопаснее. В тестовом примере вы тестируете только предопределенные входные данные и сравниваете результат, это работает хорошо до тех пор, пока все возможные крайние случаи были проверены. Первый вариант просто проверяет каждый ввод и, таким образом, проверяет «живые» значения, он очень быстро отфильтровывает ошибки, однако он снижает производительность.

In Code Complete Стив Макконнелл узнает, что первый метод может быть успешно использован для фильтрации ошибок в 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 голосов
/ 23 августа 2008

Оба механизма имеют значение. Любая приличная тестовая среда в любом случае перехватит стандартную assert (), поэтому тестовый запуск, приводящий к неудаче assert, приведет к неудачному тесту.

Обычно у меня есть серия утверждений в начале каждого метода c ++ с комментарием «// предварительные условия»; это просто проверка работоспособности состояния, которое, как я ожидаю, будет у объекта при вызове метода. Они хорошо вписываются в любую инфраструктуру TDD, потому что они работают не только во время выполнения, когда вы тестируете функциональность, но и во время тестирования.

1 голос
/ 20 августа 2008

Нет причин, по которым ваш тестовый пакет не может перехватывать утверждения, такие как в doMoreWonderfulThings Это можно сделать, либо поддержав обработчик ASSERT в механизме обратного вызова, либо тестовые утверждения содержат блок try / catch.

0 голосов
/ 28 августа 2013

В C ++ я предпочитаю метод 2 при использовании большинства платформ тестирования. Обычно это облегчает понимание отчетов об ошибках. Это неоценимо, когда тест написан через месяцы или годы после его написания.

Моя причина в том, что большинство сред тестирования C ++ выводят файл и номер строки, где произошло утверждение, без какой-либо информации о трассировке стека. Таким образом, большую часть времени вы будете получать номер строки отчета внутри функции или метода, а не внутри тестового примера.

Даже если утверждение поймано и повторно заявлено от вызывающей стороны, строка отчета будет с оператором catch и не может быть где-либо близко к строке тестового примера, которая вызвала метод или функцию, которая утверждала. Это может сильно раздражать, когда заявленная функция могла использоваться несколько раз в тестовом примере.

Хотя есть исключения. Например, в тестовой среде Google есть оператор трассировки с заданной областью, который будет выводиться как часть трассировки в случае возникновения исключения. Таким образом, вы можете заключить вызов обобщенной тестовой функции в область трассировки и легко определить, в одной или двух строках, какая строка в конкретном тестовом примере не удалась.

0 голосов
/ 20 августа 2008

Я не знаю, к какому конкретному TDD-сообществу вы обращаетесь, но шаблоны TDD, с которыми я сталкивался, либо используют Assert.AreEqual () для положительных результатов, либо иным образом используют механизм ExpectedException (например, атрибуты в .NET) объявить ошибку, которая должна быть соблюдена.

...