Чтение онлайн

на главную - закладки

Жанры

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

Как правило, отчет об ошибках содержит слишком мало посторонней информации, и первой задачей при его обработке является создание как можно более короткой программы, которая выявляла бы указанную проблему. Для этого часто приходится отбрасывать большую часть представленного кода: в частности, мы обычно пытаемся исключить использование библиотек и прикладной код, который не влияет на ошибку. Конструирование такой минимальной программы часто помогает локализовать ошибку в системном коде, и такую программу стоит добавить в тестовый набор. Для того чтобы получить минимальную программу, следует удалять код до тех пор, пока не исчезнет сама ошибка, — в этот момент следует вернуть в программу последнюю исключенную часть кода. Эту процедуру следует продолжать до тех пор, пока не будут удалены все возможные фрагменты кода, не имеющие

отношения к ошибке.

Простое выполнение сотен (или десятков тысяч) тестов, созданных на основе прошлых отчетов об ошибках, может выглядеть не очень систематизированным, но на самом деле в этом случае мы действительно целенаправленно используем опыт пользователей и разработчиков. Набор регрессивных тестов представляет собой главную часть коллективной памяти группы разработчиков. При разработке крупных систем мы просто не можем рассчитывать на постоянный контакт с разработчиками исходных кодов, чтобы они объяснили нам детали проектирования и реализации. Именно регрессивные тесты не позволяют системе отклоняться от линии поведения, согласованной с разработчиками и пользователями.

26.3.2. Модульные тесты

Однако достаточно слов! Рассмотрим конкретный пример: протестируем программу для бинарного поиска. Ее спецификация из стандарта ISO приведена ниже (раздел 25.3.3.4).

template<class ForwardIterator, class T>

bool binary_search(ForwardIterator first,

ForwardIterator last,const T& value);

template<class ForwardIterator, class T, class Compare>

bool binary_search(ForwardIterator first,

ForwardIterator last,const T& value,Compare comp);

Требует. Элементы

e
из диапазона
[first, last]
разделены в соответствии с отношением
e<value
и
!(value<e)
или
comp(e,value)
и
!comp(value,e)
. Кроме того, для всех элементов
e
диапазона
[first,last]
из условия
e<value
следует
!(value<e)
, а из условия
comp(e,value)
следует
!comp(value,e)
.

Возвращает. Значение

true
, если в диапазоне
[first,last]
существует итератор
i
, удовлетворяющий условиям:
!(*I<value)&&!(value<*i)
или
comp(*i,value)==false&&comp(value,*i)==false
.

Сложность. Не более

log(last–first)+2
сравнения.

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

true
, если оно лежит в диапазоне, определенном указанными итераторами. Эти итераторы должны задавать упорядоченную последовательность. Критерием сравнения (упорядочения) является оператор
<
. Вторую версию функции
binary_search
, в которой критерий сравнения задается как дополнительный аргумент, мы оставляем читателям в качестве упражнения.

Здесь мы столкнемся только с ошибками, которые не перехватывает компилятор, поэтому примеры, подобные этому, для кого-то станут проблемой.

binary_search(1,4,5); // ошибка: int — это
не однонаправленный

// итератор

vector<int> v(10);

binary_search(v.begin,v.end,"7"); // ошибка: невозможно найти

// строку

// в векторе целых чисел

binary_search(v.begin,v.end); // ошибка: забыли значение

Как систематически протестировать функцию
binary_search
? Очевидно, мы не можем просто перебрать все аргументы, так как этими аргументами являются любые мыслимые последовательности значений любого возможного типа — количество таких тестов станет бесконечным! Итак, мы должны выбрать тесты и определить некие принципы этого выбора.

• Тест на возможные ошибки (находит большинство ошибок).

• Тест на опасные ошибки (находит ошибки, имеющие наихудшие возможные последствия).

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

binary_search
в программе, где все ответы проверяются дважды, то получить неправильный ответ от функции
binary_search
может быть более приемлемым вариантом, чем не получить никакого, поскольку во втором случае возникает бесконечный цикл. В таком случае мы могли бы приложить больше усилий, чтобы найти трюк, провоцирующий бесконечный (или очень длинный) цикл в функции
binary_search
, по сравнению с исследованием вариантов, в которых она дает неправильный ответ. Отметьте в данном контексте слово “трюк”. Помимо всего прочего, тестирование — это занятие, требующее изобретательного подхода к задаче “как заставить код работать неправильно”.

Лучшие тестировщики не только методичные, но и изворотливые люди (в хорошем смысле, конечно).

26.3.2.1. Стратегия тестирования

С чего мы начинаем испытание функции

binary_search
? Мы смотрим на ее требования, т.е. на предположения о ее входных данных. К сожалению для тестировщиков, в требованиях явно указано, что диапазон
[first,last]
должен быть упорядоченной последовательностью. Другими словами, именно вызывающий модуль должен это гарантировать, поэтому мы не имеем права испытывать функцию
binary_search
, подавая на ее вход неупорядоченную последовательность или диапазон
[first,last]
, в котором выполняется условие
last<first
. Обратите внимание на то, что в требованиях функции
binary_search
не указано, что она должна делать, если мы нарушим эти условия. В любом другом фрагменте стандарта говорится, что в этих случаях функция может генерировать исключение, но она не обязана это делать. И все же во время тестирования функции
binary_search
такие вещи следует твердо помнить, потому что, если вызывающий модуль нарушает требования функции, такой как
binary_search
, скорее всего, возникнут ошибки.

Для функции

binary_search
можно себе представить следующие виды ошибок.

• Функция ничего не возвращает (например, из-за бесконечного цикла).

• Сбой (например, неправильное разыменование, бесконечная рекурсия).

• Значение не найдено, несмотря на то, что оно находится в указанной последовательности.

• Значение найдено, несмотря на то, что оно не находится в указанной последовательности.

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

Поделиться:
Популярные книги

Эффект Фостера

Аллен Селина
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Эффект Фостера

Жена моего брата

Рам Янка
1. Черкасовы-Ольховские
Любовные романы:
современные любовные романы
6.25
рейтинг книги
Жена моего брата

Штуцер и тесак

Дроздов Анатолий Федорович
1. Штуцер и тесак
Фантастика:
боевая фантастика
альтернативная история
8.78
рейтинг книги
Штуцер и тесак

Любовь Носорога

Зайцева Мария
Любовные романы:
современные любовные романы
9.11
рейтинг книги
Любовь Носорога

Возвышение Меркурия

Кронос Александр
1. Меркурий
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия

Мастер Разума III

Кронос Александр
3. Мастер Разума
Фантастика:
героическая фантастика
попаданцы
аниме
5.25
рейтинг книги
Мастер Разума III

Виконт. Книга 4. Колонист

Юллем Евгений
Псевдоним `Испанец`
Фантастика:
фэнтези
попаданцы
аниме
7.50
рейтинг книги
Виконт. Книга 4. Колонист

Перерождение

Жгулёв Пётр Николаевич
9. Real-Rpg
Фантастика:
фэнтези
рпг
5.00
рейтинг книги
Перерождение

Имперец. Том 1 и Том 2

Романов Михаил Яковлевич
1. Имперец
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Имперец. Том 1 и Том 2

Вечная Война. Книга II

Винокуров Юрий
2. Вечная война.
Фантастика:
юмористическая фантастика
космическая фантастика
8.37
рейтинг книги
Вечная Война. Книга II

Архонт

Прокофьев Роман Юрьевич
5. Стеллар
Фантастика:
боевая фантастика
рпг
7.80
рейтинг книги
Архонт

Смерть может танцевать 4

Вальтер Макс
4. Безликий
Фантастика:
боевая фантастика
5.85
рейтинг книги
Смерть может танцевать 4

Кодекс Охотника. Книга XVI

Винокуров Юрий
16. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XVI

Утопающий во лжи 3

Жуковский Лев
3. Утопающий во лжи
Фантастика:
фэнтези
рпг
5.00
рейтинг книги
Утопающий во лжи 3