2008-11-06 6 views
15

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

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, тест, так что я задавался вопросом, есть ли какой-то утверждать магии тоже. ..

+0

Я знаю, что это старая тема, но я добавил решение, которое я придумал, что помогло мне на работе. – grokus 2011-07-26 22:26:19

ответ

5

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

0

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

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

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

Так что сделайте себе одолжение и перекодируйте функцию assert, которая генерирует исключения. Здесь есть одно: How can I assert() without using abort()?

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

+12

Если вы считаете, что это неправильно, вы утверждаете. Утверждения должны использоваться только для вещей, которые должны быть истинными, или это ошибка * программиста *, см. Пример, приведенный в вопросе. Если программист использует функцию неправильно, вы хотели бы знать это как можно скорее. – 2008-11-07 12:44:17

+1

Я согласен с Андреасом. Утверждения и по возможности static_asserts не должны заменяться исключениями. Они предназначены для обнаружения ошибок программистов. – 2008-11-07 20:08:24

9

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

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

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

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

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

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

3

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

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

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

12

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

Boost UTF использует boost::execution_monitor (in <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).

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

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

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

  • Если в версии Release для msvc может возникнуть какая-либо утвердительная или другая фатальная ошибка , она не будет обнаружена. (см. документы execute_monitor).

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

См:

Кроме того, благодаря Геннадию Розенталь (Автор повышающего Test), если вам случится прочитать, Великая работа !!

2

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

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

Смежные вопросы