Тестирование для утверждения в рамках теста Boost Test

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

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, поэтому я подумал, есть ли еще какая-то маска утверждения...

Ответы

Ответ 1

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

Ответ 2

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

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

так:

#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. (хотя это, кажется, цель execute_monitor).

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

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

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

  • Если в версии Release для msvc какая-то утвердительная или другая фатальная ошибка в любом случае это не будет поймано. (см. документы исполнения_очистки).

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

См:

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

Ответ 3

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

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

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

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

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