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

на главную

Жанры

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

Итак, вы написали функцию для бинарного поиска. Если нет, то вернитесь к предыдущему абзацу. Почему вы уверены, что ваша функция поиска корректна? Изложите свои аргументы, обосновывающие корректность программы.

Вы уверены в своих аргументах? Нет ли слабых мест в вашей аргументации? Это была тривиальная программа, реализующая очень простой и хорошо известный алгоритм. Исходный текст вашего компилятора занимает около 200 Кбайт памяти, исходный текст вашей операционной системы — от 10 до 50 Мбайт, а код, обеспечивающий безопасность полета самолета, на котором вы отправитесь отдыхать во время ваших следующих каникул или на конференцию, составляет от 500 Кбайт до 2 Мбайт. Это вас утешает? Как применить методы, которые вы использовали для проверки функции
бинарного поиска, к реальному программному обеспечению, имеющему гораздо большие размеры.

Любопытно, что, несмотря на всю сложность, большую часть времени большая часть программного обеспечения работает правильно. К этому числу критически важных требований программы мы не относим игровые программы на персональных компьютерах. Следует подчеркнуть, что программное обеспечение с особыми требованиями к безопасности практически всегда работает корректно. Мы не будем упоминать в этой связи программное обеспечение бортовых компьютеров авиалайнеров или автомобилей из-за того, что за последнее десятилетие были зарегистрированы сбои в их работе. Рассказы о банковском программном обеспечении, вышедшем из строя из-за чека на 0,00 доллара, в настоящее время устарели; такие вещи больше не происходят. И все же программное обеспечение пишут такие же люди, как вы. Вы знаете, что делаете ошибки; но если мы можем делать ошибки, то почему следует думать, что “они” их не делают?

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

Можно было бы пофилософствовать и подискутировать о том, следует ли считать неожиданные ошибки реальными ошибками, но давайте не будем этого делать. Для разработчиков системы выгоднее сразу выяснить, как сделать свои системы более надежными.

26.1.1. Предостережение

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

26.2. Доказательства

Постойте! Почему бы просто не доказать, что наши программы корректны, и не возиться с тестами? Как лаконично указал Эдсгер Дейкстра (Edsger Dijkstra): “Тестирование может выявить наличие ошибок, а не их отсутствие”. Это приводит к очевидному желанию доказать корректность программ так, как математики доказывают теоремы.

К сожалению, доказательство корректности нетривиальных программ выходит за пределы современных возможностей (за исключением некоторых очень ограниченных прикладных областей), само доказательство может содержать ошибки (как и математические теоремы), и вся теория и практика доказательства корректности программ являются весьма сложными. Итак, поскольку мы можем структурировать свои программы, то можем раздумывать о них и убеждаться,
что они работают правильно. Однако мы также тестируем программы (раздел 26.3) и пытаемся организовать код так, чтобы он был устойчив к оставшимся ошибкам (раздел 26.4).

26.3. Тестирование

В разделе 5.11 мы назвали тестирование систематическим поиском ошибок. Рассмотрим методы такого поиска.

Различают тестирование модулей (unit testing) и тестирование систем (system testing). Модулем называется функция или класс, являющиеся частью полной программы. Если мы тестируем такие модули по отдельности, то знаем, где искать проблемы в случае обнаружения ошибок; все ошибки, которые мы можем обнаружить, находятся в проверяемом модуле (или в коде, который мы используем для проведения тестирования). Это контрастирует с тестированием систем, в ходе которого тестируется полная система, и мы знаем, что ошибка находится “где-то в системе”. Как правило, ошибки, найденные при тестировании систем, — при условии, что мы хорошо протестировали отдельные модули, — связаны с нежелательными взаимодействиями модулей. Ошибки в системе часто найти труднее, чем в модуле, причем на это затрачивается больше сил и времени.

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

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

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

Говоря, что кто-то (может быть, вы сами) может изменить код после того, как вы его протестируете, приводит нас к идее регрессивного тестирования. По существу, как только вы внесли изменение, сразу же повторите тестирование, чтобы убедиться, что вы ничего не разрушили. Итак, если вы улучшили модуль, то должны повторить его тестирование и, перед тем как передать законченную систему кому-то еще (или перед тем, как использовать ее самому), должны выполнить тестирование полной системы. Выполнение такого полного тестирования системы часто называют регрессивным тестированием (regression testing), поскольку оно подразумевает выполнение тестов, которые ранее уже выявили ошибки, чтобы убедиться, что они не возникли вновь. Если они возникли вновь, то программа регрессировала и ошибки следует устранить снова.

26.3.1. Регрессивные тесты

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

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

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

Аллен Селина
Любовные романы:
современные любовные романы
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