- •Оглавление
- •Об авторе
- •Благодарности
- •Предисловие
- •Глава 1. Держим оборону
- •На пути к хорошему коду
- •Готовьтесь к худшему
- •Что такое защитное программирование?
- •Этот страшный, ужасный мир
- •Технологии защитного программирования
- •Выберите хороший стиль кодирования и пользуйтесь крепкой архитектурой
- •Пишите код без спешки
- •Не верьте никому
- •Стремитесь к ясности, а не к краткости
- •Не позволяйте никому лезть туда, где ему нечего делать
- •Включайте вывод всех предупреждений при компиляции
- •Пользуйтесь средствами статического анализа
- •Применяйте безопасные структуры данных
- •Проверяйте все возвращаемые значения
- •Аккуратно обращайтесь с памятью (и другими ценными ресурсами)
- •Инициализируйте все переменные там, где вы их объявили
- •Объявляйте переменные как можно позже
- •Пользуйтесь стандартными средствами языка
- •Пользуйтесь хорошими средствами регистрации диагностических сообщений
- •Выполняйте приведение типов с осторожностью
- •Подробности
- •Ограничения
- •Какие ограничения налагать
- •Снятие ограничений
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 2. Тонкий расчет
- •Да в чем проблема?
- •Знайте своих клиентов
- •Что такое хорошее представление?
- •Размещение скобок
- •Скобки в стиле K&R
- •Расширенный стиль скобок
- •Стиль Уайтсмита (с отступами)
- •Другие стили скобок
- •Единственно верный стиль
- •Внутрифирменные стили (и когда их придерживаться)
- •Установка стандарта
- •Религиозные войны?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 3. Что в имени тебе моем?
- •Зачем нужны хорошие имена?
- •Каким объектам мы даем имена?
- •Игра в названия
- •Описательность
- •Техническая корректность
- •Идиоматичность
- •Тактичность
- •Технические подробности
- •Имена переменных
- •Имена функций
- •Имена типов
- •Пространства имен
- •Имена макросов
- •Имена файлов
- •Роза пахнет розой
- •Соблюдайте единообразие
- •Связывайте имя с содержимым
- •Извлекайте выгоду из выбора имени
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 4. Литературоведение
- •Самодокументируемый код
- •Техника написания самодокументируемого кода
- •Пишите простой код с хорошим форматированием
- •Выбирайте осмысленные имена
- •Разбивайте код на самостоятельные функции
- •Выбирайте содержательные имена типов
- •Применяйте именованные константы
- •Выделяйте важные фрагменты кода
- •Объединяйте взаимосвязанные данные
- •Снабжайте файлы заголовками
- •Правильно обрабатывайте ошибки
- •Пишите осмысленные комментарии
- •Практические методологии самодокументирования
- •Грамотное программирование
- •Инструментарий документирования
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 5. Заметки на полях
- •Что есть комментарий в коде?
- •Как выглядят комментарии?
- •Сколько комментариев требуется?
- •Что помещать в комментарии?
- •Не нужно описывать код
- •Не подменяйте код
- •Как сделать комментарии полезными
- •Не отвлекаться
- •На практике
- •Замечание об эстетичности
- •Единообразие
- •Четкие блочные комментарии
- •Отступы в комментариях
- •Комментарии в конце строки
- •Помощь в чтении кода
- •Стиль должен обеспечивать легкость сопровождения
- •Границы
- •Флажки
- •Комментарии в заголовке файла
- •Работа с комментариями
- •Помощь при написании программ
- •Заметки об исправлении ошибок
- •Устаревание комментариев
- •Сопровождение и бессодержательные комментарии
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 6. Людям свойственно ошибаться
- •Откуда что берется
- •Механизмы сообщения об ошибках
- •Без обработки ошибок
- •Возвращаемые значения
- •Переменные, содержащие состояние ошибки
- •Исключения
- •Сигналы
- •Обнаружение ошибок
- •Обработка ошибок
- •Когда обрабатывать ошибки
- •Варианты реагирования
- •Последствия для кода
- •Подымаем скандал
- •Управление ошибками
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 7. Инструментарий программиста
- •Что такое инструмент программирования?
- •А зачем они нужны – инструменты?
- •Электроинструменты
- •Выясните, каковы его возможности
- •Научитесь им управлять
- •Выясните, для каких задач он пригоден
- •Убедитесь, что он работает
- •Имейте четкие данные о том, как получить дополнительные сведения
- •Узнайте, как получить новые версии
- •Какой инструмент необходим?
- •Средства редактирования исходного кода
- •Средства построения кода
- •Инструменты для отладки и тестирования
- •Средства поддержки языка
- •Инструменты различного назначения
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 8. Время испытаний
- •Проверка на подлинность
- •Кто, что, когда, зачем?
- •Зачем тестировать
- •Кому тестировать
- •В чем состоит тестирование
- •Когда тестировать
- •Типы тестирования
- •Выбор контрольных примеров для блочного тестирования
- •Архитектура и тестирование
- •Руками не трогать!
- •Анатомия провала
- •Справлюсь ли я сам?
- •Система контроля ошибок
- •Обсуждение ошибок
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 9. Поиск ошибок
- •Реальные факты
- •Природа этого зверя
- •Взгляд с высоты птичьего полета
- •Взгляд с поверхности земли
- •Взгляд из глубины
- •Борьба с вредителями
- •Обходная дорога
- •Правильный путь
- •Охота за ошибками
- •Ошибки этапа компиляции
- •Ошибки этапа исполнения
- •Как исправлять ошибки
- •Профилактика
- •Отладчик
- •Средство проверки доступа к памяти
- •Трассировщик системных вызовов
- •Дамп памяти
- •Журналирование
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 10. Код, который построил Джек
- •Языковые барьеры
- •Интерпретируемые языки
- •Компилируемые языки
- •Делаем слона из мухи
- •Выполнение сборки
- •Что должна уметь хорошая система сборки?
- •Простота
- •Единообразие
- •Повторяемость и надежность
- •Атомарность
- •Борьба с ошибками
- •Механика сборки
- •Выбор целей
- •Уборка
- •Зависимости
- •Автоматическая сборка
- •Конфигурация сборки
- •Рекурсивное применение make
- •Мастер на все руки
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 11. Жажда скорости
- •Что такое оптимизация?
- •От чего страдает оптимальность кода?
- •Доводы против оптимизации
- •Альтернативы
- •Нужна ли оптимизация
- •Технические подробности
- •Убедитесь, что нужна оптимизация
- •Определите самую медленную часть кода
- •Тестирование кода
- •Оптимизация кода
- •После оптимизации
- •Методы оптимизации
- •Конструктивные изменения
- •Модификация кода
- •Как писать эффективный код
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 12. Комплекс незащищенности
- •Риски
- •Наши оппоненты
- •Оправдания, оправдания
- •Ощущение незащищенности
- •Опасный проект и архитектура
- •Переполнение буфера
- •Встроенные строки запросов
- •Условия гонки
- •Целочисленное переполнение
- •Дела защитные
- •Технология установки системы
- •Технология конструирования программного обеспечения
- •Технологии реализации кода
- •Технологии процедуры
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 13. Важность проектирования
- •Программирование как конструкторская работа
- •Что нужно проектировать?
- •Хороший проект программного продукта
- •Простота
- •Элегантность
- •Модульность
- •Хорошие интерфейсы
- •Расширяемость
- •Избегайте дублирования
- •Переносимость
- •Идиоматичность
- •Документированность
- •Как проектировать код
- •Методы и процедуры проектирования
- •Инструменты проектирования
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 14. Программная архитектура
- •Что такое программная архитектура?
- •План программы
- •Точки зрения
- •Где и когда этим заниматься?
- •Для чего она применяется?
- •Компоненты и соединения
- •Какими качествами должна обладать архитектура?
- •Архитектурные стили
- •Без архитектуры
- •Многоуровневая архитектура
- •Архитектура с каналами и фильтрами
- •Архитектура клиент/сервер
- •Компонентная архитектура
- •Каркасы
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 15. Программное обеспечение – эволюция или революция?
- •Гниение программного обеспечения
- •Тревожные симптомы
- •Как развивается код?
- •Вера в невозможное
- •Как с этим бороться?
- •Как писать новый код
- •Сопровождение существующего кода
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 16. Кодеры
- •Мартышкин труд
- •Нетерпеливый
- •Кодер (Code Monkey)
- •Гуру
- •Псевдогуру
- •Высокомерный гений
- •Ковбой
- •Плановик
- •Ветеран
- •Фанатик
- •Монокультурный программист
- •Лодырь
- •Руководитель поневоле
- •Идеальный программист
- •И что из этого следует?
- •Для глупцов
- •Резюме
- •План действий
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 17. Вместе мы – сила
- •Команды – общий взгляд
- •Организация команды
- •Методы управления
- •Разделение ответственности
- •Организация и структура кода
- •Инструменты для групповой работы
- •Болезни, которым подвержены команды
- •Вавилонская башня
- •Диктатура
- •Демократия
- •Большой Каньон
- •Зыбучие пески
- •Лемминги
- •Личное мастерство и качества, необходимые для работы в команде
- •Общение
- •Скромность
- •Разрешение конфликтов
- •Обучение и приспособляемость
- •Знание пределов своих возможностей
- •Принципы групповой работы
- •Коллективное владение кодом
- •Нормы кодирования
- •Определите, что считать успехом
- •Установите ответственность
- •Избегайте истощения
- •Жизненный цикл команды
- •Создание команды
- •Рост команды
- •Групповая работа
- •Роспуск команды
- •Резюме
- •План действий
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 18. Защита исходного кода
- •Управление версиями исходного кода
- •Контроль версий
- •Контроль доступа
- •Работа с хранилищем
- •Пусть растут деревья
- •Краткая история систем контроля за исходным кодом
- •Управление конфигурацией
- •Резервное копирование
- •Выпуск исходного кода
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 19. Спецификации
- •Что же это такое, конкретно?
- •Типы спецификаций
- •Спецификация требований
- •Функциональная спецификация
- •Спецификация системной архитектуры
- •Спецификация интерфейса пользователя
- •Проектная спецификация
- •Спецификация тестирования
- •Что должны содержать спецификации?
- •Процесс составления спецификаций
- •Почему мы не пишем спецификации?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Когда проводить рецензирование?
- •Нужно ли рецензировать
- •Какой код рецензировать
- •Проведение рецензирования кода
- •Рецензирование на собраниях
- •Интеграционное рецензирование
- •Пересмотрите свое отношение
- •Позиция автора
- •Позиция рецензента
- •Идеальный код
- •За пределами рецензирования кода
- •Резюме
- •Контрольный список
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 21. Какой длины веревочка?
- •Выстрел в темноте
- •Почему трудно делать оценки?
- •Под давлением
- •Практические способы оценки
- •Игры с планами
- •Не отставай!
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 22. Рецепт программы
- •Стили программирования
- •Структурное программирование
- •Функциональное программирование
- •Логическое программирование
- •Рецепты: как и что
- •Процессы разработки
- •Каскадная модель
- •SSADM и PRINCE
- •Создание прототипов
- •Итеративная и инкрементная разработка
- •Спиральная модель
- •Другие процессы разработки
- •Спасибо, хватит!
- •Выбор процесса
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 23. За гранью возможного
- •Программирование приложений
- •Коробочные продукты
- •Заказные приложения
- •Программирование игр
- •Системное программирование
- •Встроенное программное обеспечение
- •Программирование масштаба предприятия
- •Численное программирование
- •И что дальше?
- •Резюме
- •Контрольные вопросы
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 24. Что дальше?
- •Но что же дальше?
- •Ответы и обсуждение
- •Глава 1. Держим оборону
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 2. Тонкий расчет
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 3. Что в имени тебе моем?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 4. Литературоведение
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 5. Заметки на полях
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 6. Людям свойственно ошибаться
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 7. Инструментарий программиста
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 8. Время испытаний
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 9. Поиск ошибок
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 10. Код, который построил Джек
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 11. Жажда скорости
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 12. Комплекс незащищенности
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 13. Важность проектирования
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 14. Программная архитектура
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 15. Программное обеспечение – эволюция или революция?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 16. Кодеры
- •Вопросы для размышления
- •Глава 17. Вместе мы – сила
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 18. Защита исходного кода
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 19. Спецификации
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 20. Рецензия на отстрел
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 21. Какой длины веревочка?
- •Вопросы для размышления
- •Вопросы личного характера
- •Глава 22. Рецепт программы
- •Вопросы для размышления
- •Глава 23. За гранью возможного
- •Вопросы для размышления
- •Вопросы личного характера
- •Библиография
- •Алфавитный указатель
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
186m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Глава 8. Время испытанийClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Если вы хорошо программируете, то тестирование будет отнимать у вас гораздо больше времени, чем отладка. Поэтому данная глава предшествует той, где речь идет об отладке.
На протяжении всего процесса разработки программного продукта тестирование имеет разные цели:
•Большое количество документов проходит через стадию тестирова% ния (которую обычно называют процедурой рецензирования). Бла% годаря этому, например, гарантируется, что спецификация техни% ческих требований корректно отражает потребности клиента, функ% циональные спецификации отражают технические требования, спецификации различных подсистем достаточно полно отражают функциональные спецификации и т. д.
•Затем, естественно, код реализации тестируется на машине разра% ботчика. Тестирование проходит несколько уровней: начинают с по% строчного тестирования каждой функции, затем переходят к тести% рованию отдельных модулей, а потом к интегрированным тестам объединенных секций кода.
•Наконец, тестируется конечный продукт. Хотя на этом уровне кос% венно тестируются (или должны тестироваться) все разработанные программные компоненты, цель этих тестов иная. Здесь нас инте% ресует, работает или нет программа как единое целое в соответст% вии со спецификациями.1
Тесты продукта могут проверять целый ряд вещей. Прежде всего они проверяют, что система функционирует предполагаемым обра% зом. Проверяется также корректность установки (если это коробоч% ный продукт) и его готовность к работе.
Такого рода тестирование проводит отдел контроля качества (QA). Его задача – разобраться, что должен делать продукт, и проверить, делает ли он это, а также проверить выполнение установленных для него критериев качества.
В данной главе мы сосредоточимся на центральной части – как долж% ны тестировать свой код разработчики программного продукта. Про% чие виды тестирования составляют отдельные крупные темы, освеще% ние которых не входит в задачи этой книги.
Кто, что, когда, зачем?
Чтобы тестирование программного обеспечения было эффективным, нужно разобраться, для чего мы тестируем, кто этим занимается, что подразумевает эта процедура и когда можно считать ее законченной.
1Поскольку очевидно, что корректное поведение было заранее тщательно
зафиксировано в спецификации – или не так?
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Кто,m |
что, когда, зачем? |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
187Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Контроль качества
QA: контроль качества. Звучит тягостно, правда? Но кто эти люди или что это за работа? Так называют и некую группу пер% сонала, и технологию разработки. Чтобы разобраться с QA, нуж% но отделить обыденную речь и заблуждения от правильного опре% деления.
Иногда путают QA с тестированием, однако между ними есть су% щественная разница. Тестирование служит выявлению ошибоч% ной работы программного продукта, противоречащей специфи% кации. Задача QA – профилактика. Это система мер, воздейству% ющих на процедуры и практику разработки с целью получить в итоге программное обеспечение высокого качества. Тестирова% ние – лишь малая составная часть QA, потому что качество про% граммного продукта определяется не только низким содержани% ем в нем ошибок. Контроль качества означает своевременный выпуск продукции, соблюдение бюджетных расходов, выполне% ние всех требований и ожиданий (последние могут не совпа% дать). К сожалению, современная программная индустрия вы% пускает не так много продукции высокого качества.
Кто отвечает за качество программного обеспечения? Подразде% ление компании, проводящее тестирование (часто оно называет% ся отделом QA), – это группа людей, занятых тестированием про% дукта. Их мнение о том, допустимо ли выпустить продукт с теку% щим уровнем качества, является решающим. Это существенный элемент в общей картине качества, но не единственный. Все, кто участвует в процессе разработки, так или иначе определяют каче% ство конечного продукта; качество – не наклейка, которую мож% но прицепить к готовому коду.
Ответственность за контроль качества программного продукта часто лежит на тех же людях, которые выполняют его тестиро% вание. В противном случае за общий контроль качества отвеча% ют менеджеры проекта, а тестеры тестируют сами по себе.
Зачем тестировать
Нам, разработчикам программного обеспечения, процедура тестирова% ния нужна по ряду причин: чтобы обнаружить неисправности и устра% нить их, а также гарантировать отсутствие тех же неисправностей в следующих версиях.
Учтите, что тестирование не может доказать отсутствие неисправно% стей – только их наличие. Если при тестировании вы не обнаружили ошибок, это не значит, что их нет; просто вы пока не сумели их найти.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
188m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Глава 8. Время испытанийClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Тестирование может вскрыть только наличие ошибок. Оно не может дока& зать отсутствие неисправностей. Не поддавайтесь ложному чувству спо& койствия, если код прошел ряд неадекватных тестов.
Тестирование в конце цикла разработки продукта может быть обу% словлено другими мотивами. Наряду с проверкой корректности про% граммного продукта и отсутствия в нем неисправностей может потре% боваться проверить его годность, т. е. соответствие первоначальным техническим требованиям, как доказательство его готовности к вы% пуску. Проверка годности (validation) – один из видов приемо%сдаточ% ных испытаний.
Кому тестировать
Программист, написавший код, обязан сам его протестировать. Зару% бите это себе на носу и каждое утро по 10 минут разглядывайте его в зеркало.
Есть много программистов, которые неправильно представляют себе систему испытаний, существующую в условиях промышленного про% изводства программ, а потому поспешно лепят код и сдают его в служ% бу контроля качества, не потрудившись предварительно протестиро% вать его самостоятельно. Это безответственный и непрофессиональный подход. В конечном итоге он потребует больше времени и усилий, чем при правильном тестировании. Включать непротестированный код в готовый продукт – полная глупость, а передавать его в службу кон% троля качества – немногим лучше. Действительно, задача службы контроля качества – тестирование, но тестирование всего продукта, а не тех новых строчек, которые вы написали. Служба контроля каче% ства может найти какие%нибудь глупые ошибки, которые вы пропус% тили и которые могут проявиться непонятным и неожиданным обра% зом. Но ее задача – поиск более фундаментальных ошибок, а не тех, что могли быть обнаружены ранее и являются результатом неаккурат% ной работы программистов.
Тестируйте каждый написанный вами фрагмент кода. Не рассчитывайте, что кто&то другой сделает это за вас.
В чем состоит тестирование
Разрабатывая программный продукт, мы создаем отдельные функ% ции, структуры данных, классы и собираем из них работающую систе% му. Основная стратегия тестирования состоит в том, чтобы опробовать весь этот код и проверить его работоспособность, написав некий допол% нительный код – тестовый. Это определенная оснастка, с помощью которой тестируемый объект вертят и так и сяк, провоцируя на реак% цию и убеждаясь, что реакция правильная.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Кто,m |
что, когда, зачем? |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
189Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Тестовый код пишут для каждого уровня системы, проверяя каждый важный класс и функцию вплоть до объединенных структур, образо% ванных из этих мелких частей. Для каждого теста нужно ясно опреде% лить:
•Какой именно фрагмент кода тестируется. Хорошо, если есть ясные модули с четко обозначенными границами; тогда вашими точками тестирования являются интерфейсы. Нечеткие и сложные интер% фейсы делают нечетким и сложным тестирование.
•Метод, применяемый для тестирования (см. раздел «Типы тестиро% вания» на стр. 195).
•В какой момент остановиться. Это сложный и важный вопрос, по% тому что иначе процесс может оказаться бесконечным. Когда мож% но принять решение, что проведено достаточно тестов?
Другая распространенная стратегия тестирования – это внимательное изучение кода с целью определения его корректности. Поскольку это делает человек, возможны ошибки; кроме того, необходимо четко оп% ределить требования. Проверка кода – это стандартный тип эксперти% зы (см. главу 20). Полезно пользоваться для проверки кода специаль% ным инструментарием, но он не может автоматически выполнить за вас всю работу. Очень часто такая экспертиза проводится от случая к случаю. Предпочтительнее программные тесты; у них много преиму% ществ, которые мы обсудим в этой главе. Самое эффективное – соче% тать оба метода.
Когда тестировать
Тестируйте код по мере написания, стараясь обнаружить ошибки как можно раньше. На самых ранних стадиях ошибки легче исправимы, оказывают меньше всего влияния на работу других программистов и наименее опасны. Тщательное тестирование на ранних стадиях рабо% ты – самый эффективный способ повысить качество программного продукта.
Стоимость ошибки растет по мере того, как она продолжает существо% вание в процессе разработки,1 поэтому важно начать тестирование ко% да как можно раньше – во время (или до начала) серьезных разработок программного обеспечения. Метод разработки, основанный на тести# ровании и популяризируемый некоторыми умными программистами, пропагандирует тестирование в качестве центрального элемента со% ставления программы; тестовый код пишется прежде кода, который нужно тестировать!
1См. раздел «Экономика ошибок» на стр. 216, где подробнее обсуждается
стоимость ошибок.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
190m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Глава 8. Время испытанийClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Чтобы сделать тестирование эффективным, его нужно начинать заранее, когда выявляемые ошибки еще не могут принести большого вреда. Тестовый код можно писать раньше, чем рабочий!
Это существенный момент, который необходимо включить в техноло% гию программирования. Для каждого разрабатываемого фрагмента кода сразу пишите тест. Или сначала пишите тест. Убедитесь в том, что ваш код работает, и тогда можете свободно двигаться дальше. Если вы тут же не напишете тестовый код, то в вашем тылу останется не% проверенный и потенциально содержащий ошибки код. Это подрывает стабильность всей вашей кодовой наработки. В конечном счете вам придется иметь дело с отладчиком, а это отнимает много времени.
Откладывая написание теста на будущее, вы утрачиваете выгодное по% ложение – к тому времени вы либо подзабудете, что должен делать ваш код, либо тестировать придется отдельный модуль кода. Эффек% тивность окажется уже не столь высока. Растет также вероятность, что вы вообще забудете написать тест.
Такая стратегия тестирования влечет глубокие последствия: думая о том, чтобы написать некий новый код, вы должны одновременно ре% шать, как его протестировать. Это в лучшую сторону отражается на проектировании кода; почему – мы рассмотрим в разделе «Архитекту% ра и тестирование» на стр. 202.
Каждый раз, обнаружив ошибку, проскользнувшую сквозь преграду ваших тестов, вы должны добавить новый тест в свой комплект (пред% варительно поругав себя за то, что его не оказалось на месте вовремя). Новый тест должен подтвердить, что вы корректно исправили ошиб% ку. Он также выявит возможное появление той же ошибки в будущем; ошибки имеют обыкновение воскресать из мертвых, что часто случа% ется при последующих модификациях кода.
Пишите тесты для всех выявленных ошибок.
Итак, мы пишем тесты как можно раньше, но как часто следует их прогонять? Столь часто, сколько позволяют человеческие силы, и да% же чаще (если воспользоваться поддержкой со стороны компьютера). Чем чаще мы прогоняем тесты, тем больше шансов обнаружить ошиб% ки. Это воплощается в стратегии непрерывной сборки (см. раздел «Ав% томатическая сборка» на стр. 256) и иллюстрируется мощью про% граммных тестов (которые легко повторять многократно).
Прогоняйте свои тесты как можно чаще.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Тестироватьm |
легко… |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
Тестировать легко…
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
191Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Легко, если делать это плохо, иначе это тяжелый труд. Однако это не бездумный процесс. Чтобы проверить работоспособность конкретного фрагмента кода, требуется оснастка, которая демонстрирует:
•Корректность выходных данных при всевозможных допустимых вариантах входных.
•Обработку отказа при вводе любых недопустимых данных.
Выглядит невинно, но полностью выполнить такую проверку реально лишь для простейших функций. Обычно набор допустимых входных значений слишком велик, чтобы каждое из них можно было прове% рить отдельно. Приходится выбирать небольшой репрезентативный набор входных значений. Множество недопустимых значений обычно гораздо больше множества допустимых, поэтому в качестве недопус% тимых приходится проверять тоже какой%то набор представителей.
Проиллюстрируем это на двух примерах. Первую функцию тестиро% вать легко:
bool logical_not(bool b)
{
if (b)
return false; else
return true;
}
Допустимых входных значений два, недопустимых – нет. Тогда про% стой тест для этой функции может выглядеть так:
void test_logical_not()
{
assert(logical_not(true) == false); assert(logical_not(false) == true);
}
Однако эта функция не выполняет ничего интересного. Теперь рас% смотрим функцию для поиска наибольшего общего делителя (оставив в стороне вопрос о ее элегантности). Насколько труднее ее протестиро% вать?
int greatest_common_divisor(int a, int b)
{
int low = min(a, b); int high = max(a, b);
int gcd = 0;
for (int div = low; div > 0; div)
{
if ((low % div == 0) && (high % div == 0)) if (gcd < div)
gcd = div;
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
192m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Глава 8. Время испытанийClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
}
return gcd;
}
Этот фрагмент кода тоже невелик, но протестировать его гораздо труд% нее:
•Параметров всего два, но множество допустимых значений очень ве% лико. Не представляется возможным проверить все комбинации зна% чений; это заняло бы слишком много времени.1 Добавление в функ% цию новых параметров увеличивает сложность экспоненциально.
•В функции есть цикл. Все виды ветвления (и цикл for) увеличива% ют сложность и вероятность ошибки.
•Есть несколько условных операторов. Придется организовать ис% пытание так, чтобы код прошел через каждый вариант сочетания условий, и проверить, что они работают.
И это все для одной маленькой функции. Кстати, если вы заметили, в ней есть ошибка. Можете ее обнаружить? В случае успеха вам де% сять очков и звездочка.2
Читая код, очень легко обмануться и поверить, что он работает правиль& но. Если вы написали код, то при его чтении вы будете видеть то, что соби& рались написать, а не то, что написали на самом деле. Учитесь читать код с циничным недоверием.
Эти три проблемы не исчерпывают причин сложности тестирования кода. Есть много других факторов, увеличивающих сложность тести% рования.
Размер кода
Чем больше код, тем больше места для потенциальных ошибок и тем больше отдельных маршрутов выполнения нужно проследить, что% бы убедиться в правильности кода.
Зависимости
Протестировать маленький фрагмент кода должно быть просто. Но если для своей работы оснастка тестирования должна подключить остальной код проекта, написание любых тестов отнимет слишком много труда и времени. В таком случае вы либо вообще не тестируе% те, либо тесты оказываются ограниченными, поскольку управлять
1Чем больше входные значения, тем дольше будет выполняться цикл. Пред% положив, что int имеет разрядность 32 бита (т. е. 264 входных комбинаций) и есть прекрасная быстрая машина (допустим, что вызов функции занимает миллисекунду – чертовски мощный процессор!), обычный перебор займет около 600 миллионов лет! И это если не выводить результаты тестирования…
2О ней сказано в ответе на первый вопрос раздела «Вопросы для размышле%
ния» этой главы (стр. 615).
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
|
|
P |
|
|
|
|
|
NOW! |
o |
|
||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
Тестироватьm |
легко… |
|||||
|
|
|
|
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
193Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
всеми подключаемыми компонентами кода слишком трудно. Это пример того, как проект кода делает его тестирование невозмож# ным. Ниже мы изучим способы борьбы с этим (см. раздел «Архи% тектура и тестирование» на стр. 202).
Следующие два примера тоже демонстрируют случаи внутренних зависимостей кода.
Внешние исходные данные
Всякая зависимость состояния внешней части системы, в сущности, представляет собой еще один источник внешних данных. В отличие от параметров функции, не так легко заставить эти внешние источ% ники подавать значения, необходимые для тестирования. Общей глобальной переменной нельзя присвоить произвольное значение без того, чтобы это повлияло на другие части работающей программы.
Внешние побудительные причины
Код может реагировать не только на вызовы функций. Особенно об% ременительно, если эти факторы возникают асинхронно (в любой момент) и с произвольной частотой.
•Класс может обрабатывать обращения из других частей систе% мы, возникающие в произвольный момент.
•Аппаратные интерфейсы могут реагировать на изменения в фи% зическом состоянии устройства.
•Обмен данными с другими системами может продолжаться лю% бое время. Физические соединения подвержены воздействию по% мех, поэтому их качество может снижаться, а сетевые соедине% ния бывают ненадежными.
•Код интерфейса пользователя воспринимает движения мыши. Создание автоматизированных условий физического тестирова% ния GUI может вызвать затруднения.
Такие условия тяжело моделировать в искусственной среде тести% рования, и они могут быть особенно чувствительны к временным параметрам (например, скорости двойных щелчков мыши или ап% паратно генерируемым прерываниям).
Некоторые внешние факторы оказываются незапланированными: недостаток оперативной или дисковой памяти, отказ сетевых со% единений. Необходимо гарантировать устойчивость работы своего кода в наиболее распространенных условиях окружающей среды.
Потоки
Многопоточность еще более осложняет тестирование, поскольку вмешательство кода, выполняемого одновременно, может происхо% дить в произвольные моменты. Сложное взаимодействие последова% тельностей выполнения может привести к тому, что любые конкрет% ные прогоны тестов оказываются невоспроизводимыми. Ошибки выполнения потоков, приводящие к блокировкам и недоступности
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
|
E |
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
||
|
F |
|
|
|
|
|
|
t |
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
|
P |
|
|
|
|
|
NOW! |
o |
||
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|||
|
|
|
|
to |
|
|
|
|
|
w Click |
|
|
|
194m |
|||||
|
|
|
|
||||||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
. |
|
|
|
|
|
.c |
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
df |
|
|
n |
e |
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
Глава 8. Время испытанийClick |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
.c |
|
||
|
. |
|
|
|
|
|
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
ресурсов, трудно смоделировать, но при возникновении они вызы% вают серьезные проблемы.
Поведение многопоточной программы на мультипроцессорных сис% темах может отличаться от поведения однопроцессорных систем, моделирующих параллельные вычисления.
Эволюция
Программное обеспечение развивается, в результате чего тестирова% ние может оказаться недействительным. Если технические требова% ния точно не закреплены, ваши ранние тесты могут оказаться несо% стоятельными ко времени выпуска продукта, когда изменятся API, функциональность окажется совершенно иной, а полного набора тестов не будет, потому что разработка двигалась слишком быстро.
Нам необходимы стабильные интерфейсы как в нашем собственном коде, так и во внешнем коде, с которым мы связаны. В реальности это недостижимый идеал, поскольку код никогда не стоит на месте. Поэтому нужно строить небольшие гибкие тесты, которые можно модифицировать параллельно с кодом.
Аппаратные сбои
Ошибки бывают как в программах, так и в аппаратуре. Аппарат% ные ошибки чаще встречаются во встроенных средах, потому что в них вы ближе к железу. Диагностирование и исправление аппа% ратных ошибок может оказаться гораздо сложнее, чем программ% ных: воспроизводить их трудно, а первое подозрение естественно падает на программы.
Особые случаи сбоев
Отказ кода может проявляться различными неожиданными и стран% ными способами. Сбой программы не просто приводит к получению неверных результатов – приходится сталкиваться со многими ве% щами: бесконечными циклами, блокировками, недоступностью ре% сурсов, аварийным завершением, блокировками ОС и прочими мерз% кими вещами, делающими тестирование разнообразным и волную% щим занятием. Определенные программные ошибки могут даже вы% вести из строя оборудование!1 Пишите тесты, которые это проверяют.
Написание кода, осуществляющего тестирование, – нелегкая задача. По мере соединения компонент друг с другом и появления зависимо% стей между ними сложность программного продукта растет с экспонен% циальной скоростью. Все эти проблемы сливаются и сильно осложняют вам жизнь. В какой%то момент написать структуру для всеобъемлющего тестирования программного продукта становится не только сложно, но и технически нереально. У вас не будет столько времени и ресурсов,
1Это не смешно. У процессора 68000 была недокументированная функция останова и возгорания – операция тестирования шины, которая быстро пе%
ребирала адресные линии, вызывая перегрев и возгорание микросхемы.