2013-06-17 1 views
3

У меня есть функция класса (Class member), которую я хочу избежать краха приложения из-за двусмысленности. Для этого я добавил попытку поймать Бок, как показано ниже:попробуйте поймать блоки с boost.Test

void getGene(unsigned int position){ 
    T val; 
    try { 
     val = _genome.at(_isCircular ? position % _genome.size() : position); 
    } 
    catch (std::exception& e) { 
     std::cerr << "Error in [" << __PRETTY_FUNCTION__ << "]: " 
        << e.what() << std::endl; 
     exit(1); 
    } 
    return val; 
} 

Теперь я хотел бы добавить модульный тест Boost, который я думал, что делать что-то вроде

BOOST_AUTO_TEST_CASE(nonCircularGenome_test){ 

    // set size to 10 
    test.setSize(10); 
    // set non circular 
    test.setNonCircular();  

    // gene at site # 12 does not exist in a 10-site long genome, must throw an exception 
    BOOST_CHECK_THROW(test.getGene(12), std::out_of_range); 

Проблема заключается в том, Я не могу заставить обе эти вещи работать. Блок try-catch хорошо работает в настройке релиза. Однако этот тест работает, только если я удаляю блок try-catch и позволяю функции вызывать исключение.

Каков наилучший способ заставить обе эти вещи работать, чтобы пользователь запрашивал правильную ошибку на ходу, а тесты проверяли явно при отладке? Одним из способов является использование # ifdef/# endif блоков DEBUG, но я хочу избежать макросов препроцессора.

Спасибо заранее,

Нихилу

ответ

3

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

Прежде всего, вы должны определить, каковы предпосылки вашей функции: getGene() всегда ожидает, что position будет действительным? Ожидает ли его клиентов никогда недействительные позиции?

Если это так, клиент, который предоставляет недействительную позицию (даже если клиент является тестовой процедурой), разорвал контракт с помощью getGene() (в частности, он нарушил свое предварительное условие) и нарушил Контракт - это неопределенное поведение по определению. Вы не можете проверить неопределенное поведение, поэтому вам следует удалить свой тест.

С другой стороны, если ваша функция имеет широкий контракт, то есть позволяет клиентам передавать в любую позицию (даже недействительные) и (a) выдает исключение или (b) возвращает код ошибки для сообщать об ошибке, когда позиция недействительна, тогда строка exit(1) не должна быть там, потому что вы покидаете программу, и управление не возвращается обратно вызывающему абоненту.

Одна возможность состоит в том, чтобы вновь бросить исключение после входа диагностики:

T getGene(unsigned int position){ 
    T val; 
    try { 
     val = _genome.at(_isCircular ? position % _genome.size() : position); 
    } 
    catch (std::exception& e) { 
     std::cerr << "Error in [" << __PRETTY_FUNCTION__ << "]: " 
        << e.what() << std::endl; 

     throw; 
//  ^^^^^ 
    } 
    return val; 
} 

И если вам не нужно печатать диагностическое, пусть исключением, естественно, распространяются:

T getGene(unsigned int position){ 
    return _genome.at(_isCircular ? position % _genome.size() : position); 
} 
+0

+ 1, но некоторые детали ... вы * можете * иногда проверять неопределенное поведение, это зависит от того, какое неопределенное поведение. В терминах Lakos вы можете тестировать * мягкое * неопределенное поведение (т. Е. Из-за контрактных вызовов в вашу библиотеку), но не * жесткое * неопределенное поведение (т. Е. Что-то, что уже вызвало неопределенное поведение в библиотеке и/или языке). На самом деле есть предложение ([n3604] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3604.pdf)), чтобы добавить некоторую поддержку этому в уровень языка и [BSL] (https://github.com/bloomberg/bsl) имеет реализацию уровня библиотеки. –

+0

@ DavidRodríguez-dribeas: Должен признаться, я слишком давно прочитал это интересное предложение, чтобы точно запомнить его содержание.Теперь я немного озадачен, поэтому я спрошу вас, если вы не возражаете: если нарушение предварительного условия является неопределенным поведением, то вызов этой функции путем нарушения этого предварительного условия приведет к запуску неопределенного поведения, а это означает, что утверждение теста не может ожидать согласованного результата. Неужели я не понимаю этого? Или наш тест должен запускаться в контексте, где обычно неопределенное поведение теперь является четко определенным поведением (например, выбрасыванием исключения)? –

+0

Дело в том, что в вашей функции вы можете проверить предварительные условия * до *, ваша функция сама вызывает неопределенное поведение где-то еще с помощью одного из макросов assert. Структура в Bloomberg/BSL и предложение позволяют конфигурировать то, что происходит, когда утверждение запускается из 'main' (ну, из любой точки кода в любом случае). Теперь в тестовом наборе (вы можете проверить тесты BSL) вы можете настроить обработчик assert как уникальное исключение, и таким образом вы можете добавить тесты, которые подтверждают, что ваш код способен * обнаруживать * UB на вашем собственном интерфейсе [. ..] –