Тестирование на assert в рамках Boost Test - PullRequest
19 голосов
/ 07 ноября 2008

Я использую платформу Boost Test для модульного тестирования моего кода C ++ и спрашиваю себя, можно ли проверить, будет ли функция утверждать? Да, звучит немного странно, но терпите меня! Многие из моих функций проверяют входные параметры при входе, утверждая, что они недействительны, и было бы полезно проверить это. Например:

void MyFunction(int param)
{
    assert(param > 0); // param cannot be less than 1
    ...
}

Я хотел бы иметь возможность сделать что-то вроде этого:

BOOST_CHECK_ASSERT(MyFunction(0), true);
BOOST_CHECK_ASSERT(MyFunction(-1), true);
BOOST_CHECK_ASSERT(MyFunction(1), false);
...

Вы можете проверить наличие исключений с помощью Boost Test, поэтому мне стало интересно, была ли там какая-то магия утверждения ...

Ответы [ 6 ]

13 голосов
/ 20 ноября 2009

Имея ту же проблему, я покопался в документации (и коде) и нашел "решение".

Boost UTF использует boost::execution_monitor<boost/test/execution_monitor.hpp>). Это разработано с целью поймать все, что может произойти во время выполнения теста. Когда найден execute_monitor перехватывает его и выдает boost::execution_exception. Таким образом, используя BOOST_REQUIRE_THROW, вы можете утверждать неудачу утверждения.

так:

#include <boost/test/unit_test.hpp>
#include <boost/test/execution_monitor.hpp>  // for execution_exception

BOOST_AUTO_TEST_CASE(case_1)
{
  BOOST_REQUIRE_THROW(function_w_failing_assert(),
                      boost::execution_exception);
}

Должен сделать свое дело. (Это работает для меня.)

Однако (или отказ от ответственности):

  • Это работает для меня. То есть на Windows XP, MSVC 7.1, буст 1.41.0. Это может быть неподходящим или сломанным в вашей настройке.

  • Это может быть не намерение автора Boost Test. (хотя, похоже, это и есть цель execute_monitor).

  • Он будет одинаково обрабатывать каждую форму фатальной ошибки. Это может быть что-то, кроме вашего утверждения, терпит неудачу. В этом случае вы может пропустить ошибку при повреждении памяти и / или пропустить ошибочное утверждение.

  • Это может сломаться в будущих версиях Boost.

  • Я ожидаю, что произойдет сбой при запуске в Release config, поскольку assert будет отключен, и код, который был установлен assert для предотвращения воли запустить. В результате очень неопределенное поведение.

  • Если в конфигурации выпуска для msvc произошла ошибка типа assert или другая фатальная ошибка произойдет в любом случае, он не будет пойман. (см. документацию execute_monitor).

  • Если вы используете assert или нет, решать вам. Мне они нравятся.

См:

Также, спасибо Геннадию Розенталу (автору Boost Test), если вам случится прочитайте это, Великая Работа !!

9 голосов
/ 19 июля 2009

Есть два вида ошибок, которые я хотел бы проверить: инварианты и ошибки времени выполнения.

Инварианты - это вещи, которые всегда должны быть правдой, несмотря ни на что. Для них я использую утверждения. Такие вещи, как вы не должны передавать мне нулевой указатель для буфера вывода, который вы мне даете. Это ошибка в коде, простая и понятная. В отладочной сборке он подтвердит и даст мне возможность исправить это. В розничной сборке это вызовет нарушение прав доступа и создаст мини-дамп (Windows, по крайней мере, в моем коде) или coredump (Mac / unix). catch, который я могу сделать, не имеет смысла иметь дело с разыменованием нулевого указателя. В Windows catch (...) может подавлять нарушения прав доступа и давать пользователю ложное чувство уверенности в том, что все в порядке, когда они уже пошли ужасно, ужасно неправильно.

Это одна из причин, почему я пришел к выводу, что catch (...) - это, как правило, запах кода в C ++, и единственное разумное место, где я могу думать о том, что присутствие присутствует, - main (или WinMain) правильно прежде чем вы создадите дамп ядра и вежливо выйдите из приложения.

Ошибки во время выполнения - это такие вещи, как «Я не могу записать этот файл из-за разрешений» или «Я не могу записать этот файл, потому что диск заполнен». Для такого рода ошибок выбрасывать исключение имеет смысл, поскольку пользователь может что-то с этим сделать, например, изменить разрешение на каталог, удалить некоторые файлы или выбрать другое место для сохранения файла. Эти ошибки времени выполнения исправимы пользователем. Нарушение инварианта не может быть исправлено пользователем, только программистом. (Иногда они совпадают, но обычно это не так.)

Ваши модульные тесты должны заставить код выдавать исключения ошибок времени выполнения, которые может генерировать ваш код. Возможно, вы также захотите вызвать исключения от ваших соавторов, чтобы гарантировать, что ваша тестируемая система безопасна для исключений.

Однако я не верю, что имеет смысл пытаться заставить ваш код защищаться от инвариантов с помощью модульных тестов.

6 голосов
/ 07 ноября 2008

Я так не думаю. Вы всегда можете написать свой собственный assert, который генерирует исключение, а затем использовать BOOST_CHECK_NOTHROW () для этого исключения.

3 голосов
/ 19 июля 2009

Я думаю, что этот вопрос и некоторые из ответов путают обнаружение ошибок во время выполнения с обнаружением ошибок. Они также путают намерение и механизм.

Ошибка времени выполнения - это то, что может произойти в 100% правильной программе. Это требует обнаружения, и оно требует надлежащей отчетности и обработки, и это должно быть проверено. Ошибки также случаются, и для удобства программиста лучше ловить их рано, используя проверки предварительных условий или инвариантные проверки или случайное утверждение. Но это инструмент программиста. Сообщение об ошибке не имеет смысла для обычного пользователя, и не представляется разумным проверять поведение функции на данных, которые должным образом написанная программа никогда не передаст ей.

Что касается намерения и механизма, следует отметить, что исключение не является чем-то волшебным. Некоторое время назад Питер Димов сказал в списке рассылки Boost (приблизительно), что «исключения - это просто нелокальный механизм перехода». И это очень верно. Если у вас есть приложение, в котором можно продолжить работу после некоторой внутренней ошибки, без риска того, что что-то будет повреждено до восстановления, вы можете реализовать пользовательское утверждение, которое вызывает исключение C ++. Но это не изменит намерений и не сделает проверку утверждений гораздо более разумной.

2 голосов
/ 27 июля 2011

На работе я столкнулся с той же проблемой. Мое решение состоит в том, чтобы использовать флаг компиляции. Когда мой флаг GROKUS_TESTABLE установлен, мой GROKUS_ASSERT превращается в исключение, а с помощью Boost вы можете тестировать пути кода, которые выдают исключения. Когда GROKUS_TESTABLE выключен, GROKUS_ASSERT переводится в c ++ assert ().

#if GROKUS_TESTABLE
#define GROKUS_ASSERT ... // exception
#define GROKUS_CHECK_THROW    BOOST_CHECK_THROW
#else
#define GROKUS_ASSERT ... // assert
#define GROKUS_CHECK_THROW(statement, exception)  {}  // no-op
#endif

Моя первоначальная мотивация состояла в том, чтобы помочь отладке, то есть assert () может быть быстро отлажен, а исключения часто труднее отлаживать в gdb. Кажется, мой флаг компиляции довольно хорошо сочетает отлаживаемость и тестируемость.

Надеюсь, это поможет

0 голосов
/ 07 ноября 2008

Извините, но вы атакуете свою проблему неправильно.

«утверждать» - порождение дьявола (a.k.a. «C») и бесполезно для любого языка, у которого есть надлежащие исключения. Ваааааааааа, лучше переопределить функциональность, похожую на assert, с исключениями. Таким образом, вы фактически получаете возможность правильно обрабатывать ошибки (включая надлежащие процедуры очистки) или запускать их по желанию (для модульного тестирования).

Кроме того, если ваш код запускается в Windows, когда вы проваливаете утверждение, вы получаете бесполезное всплывающее окно, предлагающее вам отладить / отменить / повторить попытку. Отлично подходит для автоматизированных юнит-тестов.

Так что сделайте себе одолжение и перекодируйте функцию assert, которая выдает исключения. Здесь есть один: Как я могу утверждать () без использования abort ()?

Оберните его в макрос, чтобы получить _ _FILE _ _ и _ _ LINE _ _ (полезно для отладки), и все готово.

...