Лекция 1. Основы тестирования программного обеспечения
Зачем тестировать программное обеспечение
Программное обеспечение создаётся для решения задач пользователя, но сам факт успешного запуска программы ещё не означает, что она качественная. Приложение может работать в обычном сценарии и терять данные при ошибочном вводе, неверно рассчитывать стоимость на границе диапазона, раскрывать конфиденциальную информацию или переставать отвечать при росте нагрузки.
Тестирование программного обеспечения — процесс исследования программного продукта с целью получить информацию о его качестве и обнаружить различия между ожидаемым и фактическим поведением.
Тестирование помогает ответить на вопросы:
- выполняет ли система заявленные функции;
- корректно ли она обрабатывает ошибочные и граничные ситуации;
- можно ли безопасно выпускать новую версию;
- какие риски остаются после выполненных проверок;
- соответствует ли продукт ожиданиям пользователя и бизнеса.
Тестирование не доказывает полное отсутствие ошибок. Оно снижает неопределённость и позволяет принять обоснованное решение о готовности продукта.
Качество программного продукта
Качество нельзя свести к фразе «программа работает». Один продукт оценивают сразу по нескольким характеристикам:
- функциональная пригодность — функции решают поставленные задачи;
- надёжность — система сохраняет работоспособность и корректно восстанавливается;
- производительность — время ответа и потребление ресурсов приемлемы;
- удобство использования — пользователь понимает интерфейс и может выполнить свою задачу;
- безопасность — данные и операции защищены от несанкционированного доступа;
- совместимость — продукт взаимодействует с нужными системами и средами;
- сопровождаемость — программу можно изменять, анализировать и тестировать;
- переносимость — продукт можно установить или перенести в требуемую среду.
Характеристики могут конфликтовать. Например, дополнительная проверка безопасности повышает защищённость, но увеличивает время ответа. Поэтому качество всегда связано с требованиями, контекстом использования и допустимым уровнем риска.
Качество процесса и качество продукта
Качественный процесс разработки не гарантирует продукт без дефектов, но делает результат предсказуемее. Зафиксированные требования, рецензирование кода, автоматизированные тесты и контроль сборки уменьшают вероятность случайных ошибок.
При этом тестировщик оценивает не только готовый интерфейс. Проверяться могут:
- требования и макеты;
- архитектурные решения;
- исходный код;
- отдельные функции и классы;
- API и интеграции;
- собранное приложение;
- процесс установки, обновления и восстановления.
Ошибка, дефект и отказ
В профессиональной терминологии важно различать причину, проблему в артефакте и её наблюдаемое проявление.
Ошибка человека — неверное действие или решение при анализе, проектировании, программировании или эксплуатации.
Дефект — недостаток требования, модели, кода или другого артефакта, способный привести к неправильному поведению.
Отказ — наблюдаемое отклонение работающей системы от ожидаемого результата.
Пример:
- Разработчик использовал условие
age > 18, хотя доступ разрешён с 18 лет — это ошибка человека. - Неверное условие в исходном коде — дефект.
- Восемнадцатилетний пользователь не смог зарегистрироваться — отказ.
Не каждый дефект немедленно приводит к отказу. Он может находиться в редко используемой ветви, проявляться только при определённой конфигурации или требовать особого сочетания данных.
def can_register(age: int) -> bool: # Дефект: при age == 18 функция вернёт False. return age > 18Для обнаружения дефекта достаточно проверить значения рядом с границей: 17, 18 и 19. Такой выбор данных называется анализом граничных значений и подробно рассматривается во второй лекции.
Ожидаемый и фактический результат
Тест сравнивает два результата:
- ожидаемый определяется требованиями, договорённостями или моделью предметной области;
- фактический наблюдается во время выполнения проверки.
Если ожидаемый результат невозможно сформулировать, проблема может находиться не в программе, а в неполном или противоречивом требовании. Поэтому тестирование начинается до написания кода — с анализа требований.
Верификация и валидация
Верификация отвечает на вопрос: «Правильно ли мы создаём продукт?» Она проверяет, соответствуют ли промежуточные и итоговые артефакты установленным требованиям.
Примеры верификации:
- рецензирование требования;
- анализ макета интерфейса;
- code review;
- статический анализ кода;
- проверка реализации по техническому заданию.
Валидация отвечает на вопрос: «Тот ли продукт мы создаём?» Она показывает, решает ли система реальную задачу пользователя в предполагаемой среде.
Программа может пройти верификацию и всё равно оказаться неудобной или бесполезной. Например, форма строго соответствует спецификации, но пользователь не понимает, какое значение нужно вводить в обязательное поле. Формально реализация верна, а пользовательская задача не решена.
Статическое и динамическое тестирование
При статическом тестировании программный код не выполняется. Проверяются требования, документация, модели и исходный код.
К статическим методам относятся:
- рецензирование;
- инспекция;
- статический анализ;
- проверка стиля и типов;
- анализ архитектуры и зависимостей.
При динамическом тестировании запускают программу или её часть и сравнивают полученное поведение с ожидаемым.
def discount(total: float) -> float: return total * 0.9 if total >= 5000 else total
def test_discount_at_boundary(): assert discount(4999) == 4999 assert discount(5000) == 4500Статические и динамические проверки дополняют друг друга. Рецензирование может найти неоднозначное требование до реализации, а выполнение теста — показать проблему, которая проявляется только во время работы программы.
Уровни тестирования
Уровень показывает, какая часть системы является объектом проверки.
Модульное тестирование
Проверяются отдельные функции, классы или небольшие компоненты. Такие тесты обычно быстрые и выполняются разработчиками при каждом изменении кода.
def normalize_email(value: str) -> str: return value.strip().lower()
def test_normalize_email(): assert normalize_email(" User@Example.COM ") == "user@example.com"Интеграционное тестирование
Проверяется взаимодействие нескольких компонентов: сервиса и базы данных, приложения и файловой системы, клиента и внешнего API. Цель — обнаружить ошибки в форматах, протоколах, настройках и последовательности вызовов.
Системное тестирование
Проверяется собранная система целиком в среде, близкой к эксплуатационной. Тесты оценивают сквозные пользовательские сценарии и системные свойства.
Приёмочное тестирование
Заказчик, представитель бизнеса или пользователь подтверждает, что продукт решает поставленную задачу и готов к использованию. Основанием служат критерии приёмки.
Уровни не следует воспринимать как строго последовательные этапы. В современной разработке модульные, интеграционные и системные проверки выполняются регулярно и автоматизируются в общем конвейере.
Виды тестирования
Функциональное и нефункциональное
Функциональное тестирование проверяет, что делает система: регистрацию, расчёт стоимости, создание заказа, поиск и другие функции.
Нефункциональное тестирование оценивает, как система выполняет функции: производительность, безопасность, удобство, совместимость и устойчивость.
Позитивное и негативное
Позитивный тест использует корректные данные и подтверждает основной сценарий. Негативный тест проверяет реакцию на ошибочный ввод, нарушение последовательности, отсутствие прав или недоступность зависимости.
Для поля количества товара примерами будут:
- позитивный сценарий:
quantity = 2; - граничный сценарий:
quantity = 1; - негативный сценарий:
quantity = 0; - проверка типа:
quantity = "два".
Ручное и автоматизированное
Ручная проверка выполняется человеком. Она полезна при исследовании нового продукта, оценке удобства и работе с быстро меняющимся интерфейсом.
Автоматизированная проверка выполняется программой. Она полезна для повторяемых сценариев, регрессии, большого количества данных и частого запуска в CI.
Автоматизация не отменяет ручное тестирование. Она переносит повторяемую работу на инструменты, оставляя человеку исследование, анализ рисков и оценку результата.
Регрессионное тестирование
Регрессионные тесты подтверждают, что ранее работавшие функции не сломались после изменения. Полный набор может быть дорогим, поэтому проверки приоритизируют по риску, критичности функции и истории дефектов.
Повторное тестирование
После исправления дефекта выполняют повторную проверку конкретного сценария. Регрессионное тестирование шире: оно ищет побочные последствия исправления в других частях системы.
Семь принципов тестирования
1. Тестирование показывает наличие дефектов
Успешные проверки уменьшают вероятность необнаруженных проблем, но не доказывают их полное отсутствие.
2. Исчерпывающее тестирование невозможно
Количество комбинаций данных, состояний и окружений обычно слишком велико. Поэтому выбирают наиболее значимые проверки на основе риска и методов тест-дизайна.
3. Раннее тестирование экономит ресурсы
Неоднозначность требования дешевле исправить до проектирования и программирования. Чем позже найден дефект, тем больше артефактов и решений он затрагивает.
4. Дефекты склонны группироваться
Большая часть проблем часто сосредоточена в ограниченном количестве сложных или часто изменяемых компонентов. История дефектов помогает определять приоритеты.
5. Тесты теряют эффективность
Если постоянно выполнять один набор сценариев, он перестаёт находить новые классы дефектов. Тесты нужно пересматривать, дополнять новыми данными и подходами.
6. Тестирование зависит от контекста
Медицинская система, интернет-магазин и учебный проект имеют разные риски, критерии качества и допустимую глубину проверки.
7. Отсутствие обнаруженных ошибок не гарантирует успех
Технически стабильный продукт может не решать задачу пользователя. Необходимо проверять не только соответствие спецификации, но и ценность продукта.
Жизненный цикл дефекта
После обнаружения проблема проходит несколько состояний. Конкретные названия зависят от процесса команды, но типичный маршрут выглядит так:
- Новый — дефект зарегистрирован.
- На анализе — команда уточняет воспроизводимость, серьёзность и причину.
- Назначен — определён ответственный исполнитель.
- Исправлен — изменение внесено и передано на проверку.
- На повторном тестировании — тестировщик проверяет исправление.
- Закрыт — проблема устранена и подтверждена.
- Переоткрыт — отказ продолжает проявляться или исправление неполно.
Дефект также может быть отклонён как дубликат, ожидаемое поведение, невоспроизводимая проблема или изменение, которое решено не выполнять.
Серьёзность и приоритет
Серьёзность отражает влияние дефекта на работу системы. Приоритет определяет, насколько быстро его нужно исправить.
Критическая потеря данных обычно имеет и высокую серьёзность, и высокий приоритет. Опечатка на главной странице может иметь низкую серьёзность, но высокий приоритет перед публичной демонстрацией.
Роль тестировщика в команде
Тестировщик не является человеком, который подключается только после завершения разработки. Он помогает команде:
- уточнять требования и критерии приёмки;
- выявлять риски;
- проектировать тестируемую архитектуру;
- готовить данные и окружение;
- выполнять ручные и автоматизированные проверки;
- анализировать результаты и регистрировать дефекты;
- предоставлять информацию для решения о выпуске.
Ответственность за качество несёт вся команда. Разработчик отвечает за проверяемость кода и модульные тесты, аналитик — за ясность требований, специалист по эксплуатации — за стабильность среды, а тестировщик организует и углубляет процесс оценки качества.
Минимальная стратегия проверки
Перед началом тестирования полезно ответить на шесть вопросов:
- Что именно проверяется?
- Какие требования и ожидания считаются источником истины?
- Какие отказы наиболее опасны?
- Какие уровни и виды тестирования необходимы?
- Какие данные, инструменты и окружения потребуются?
- По каким критериям проверка будет считаться завершённой?
Эти ответы образуют основу тестовой стратегии. В небольшой учебной работе она может занимать несколько абзацев, а в промышленном проекте оформляться отдельным документом.
Итоги
- тестирование предоставляет информацию о качестве и рисках;
- ошибка человека может породить дефект, а дефект — наблюдаемый отказ;
- верификация проверяет соответствие требованиям, валидация — пригодность продукта;
- уровни тестирования различаются размером и назначением проверяемого объекта;
- виды тестирования выбираются по контексту и рискам;
- исчерпывающая проверка невозможна, поэтому важен обоснованный выбор сценариев;
- качество является общей ответственностью команды.
Вопросы для самопроверки
- Почему успешное выполнение всех тестов не доказывает отсутствие дефектов?
- Чем отличаются ошибка, дефект и отказ?
- В чём разница между верификацией и валидацией?
- Какие артефакты можно проверять статически?
- Чем модульный тест отличается от интеграционного?
- В чём различие функционального и нефункционального тестирования?
- Когда ручная проверка предпочтительнее автоматизированной?
- Чем повторное тестирование отличается от регрессионного?
- Почему серьёзность дефекта не всегда определяет его приоритет?
- Какие риски вы бы выделили для системы онлайн-оплаты?