Тестирование assert в рамках тестирования Boost

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

6 ответов


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


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

повышение UTF использует boost::execution_monitor (in <boost/test/execution_monitor.hpp>). Это разработано с целью поймать все, что может произойти во время выполнения теста. Когда assert найден execution_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, boost 1.41.0. Это может быть неподходящим или сломанным на вашей установке.

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

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

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

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

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

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

посмотреть:

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


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

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

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

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

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

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


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

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

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


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

надеюсь, что это помогает


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

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

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

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

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