Тестирование ПО: уровни, виды, ручное и автоматизированное

Тестирование ПО: уровни, виды, ручное и автоматизированное Полезное

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

Тестирование не доказывает, что ошибок нет: оно показывает, что на проверенных сценариях их не нашли. Ниже — уровни и виды тестирования, ручное и автоматизированное, пирамида тестов и пример на Python, где тест ловит ошибку на границе.

Мини-словарь: что путают чаще всего

Термин Что это Пример
Ошибка (error, mistake) Действие человека, которое приводит к неверному коду Разработчик написал > вместо >=
Дефект (defect, bug) Изъян в коде или документации В функции неверное условие
Сбой (failure) Видимое отклонение поведения при запуске Клиенту с заказом ровно на 3000 насчитали доставку
Отладка Поиск причины сбоя в коде и ее устранение Разработчик находит > и правит
QA (обеспечение качества) Процессы, которые предотвращают дефекты Ревью кода, стандарты, требования к тестам

Цепочка: ошибка человека -> дефект в коде -> сбой при запуске. Тестирование находит сбой, отладка — дефект, а QA шире и включает процессы, которые не дают дефекту появиться.

Еще одна пара: верификация отвечает на вопрос «сделали ли продукт правильно» (соответствует спецификации), валидация — «сделали ли правильный продукт» (решает задачу пользователя). Продукт может пройти первую и провалить вторую, если ошибка в самих требованиях.

Зачем тестировать и где границы

Тестирование дает информацию для решения, можно ли выпускать релиз и какие риски остаются. Чем раньше найден дефект, тем обычно дешевле исправление.

Три принципа из программы ISTQB, которые стоит держать в голове:

  • Исчерпывающее тестирование невозможно, поэтому проверки выбирают по рискам и техникам тест-дизайна.
  • Дефекты скапливаются: обычно большая их часть сосредоточена в небольшой части модулей.
  • Одни и те же тесты со временем перестают находить новые ошибки, набор нужно пересматривать.

Уровни тестирования

Уровень Что проверяет Кто обычно выполняет Пример
Модульное (unit) Функцию или класс в изоляции Разработчик Расчет стоимости доставки
Интеграционное Взаимодействие модулей и внешних систем Разработчик, автотестировщик Сервис заказов пишет в базу и вызывает платежный шлюз
Системное Систему целиком против требований Тестировщик Заказ через интерфейс от начала до конца
Приемочное Готовность с точки зрения заказчика Заказчик, аналитик, пользователи Бизнес подтверждает сценарий возврата

Оговорка: системное тестирование — уровень проверки любой программной системы целиком, а не только операционных систем, как иногда пишут. Границы уровней размыты: тест API с настоящей базой одни команды называют интеграционным, другие — системным.

Виды тестирования

Если уровень — это «что проверяем», то вид — «какое свойство». Функциональное тестирование проверяет, что система делает: при таких входных данных получаем ли ожидаемый результат.

Нефункциональное тестирование проверяет, как система это делает:

Вид Типичный вопрос
Нагрузочное Выдержит ли сервис ожидаемое число пользователей с приемлемым временем ответа
Стрессовое Что будет за пределами нормы и восстановится ли сервис
Объемное Не деградирует ли поиск на десятках миллионов записей
Безопасности Можно ли получить чужой заказ, подменив id в запросе
Юзабилити Найдет ли новый пользователь кнопку возврата
Совместимость Корректно ли все работает в разных браузерах и на телефоне
Надежность Не растет ли потребление памяти за сутки работы

Проверки, связанные с изменениями: дымовое (smoke) — сборка запускается и основные функции живы; ретест — исправленный дефект больше не воспроизводится. Регрессионное тестирование проверяет, что изменения не сломали работавшее, — это отдельная большая тема.

Подходы: черный, белый и серый ящик

Черный ящик — проверка поведения по требованиям без взгляда в код (классы эквивалентности, граничные значения). Белый ящик — тесты по структуре кода: ветвления, покрытие строк и условий. Серый ящик — частичное знание устройства, например схемы базы или API. Отдельно различают статическое тестирование (без запуска: ревью, статический анализ) и динамическое (с запуском).

Ручное и автоматизированное тестирование

Критерий Ручное Автоматизированное
Где сильнее Исследование, юзабилити, новые функции Повторяющиеся проверки, расчеты, API, нагрузка
Стоимость старта Низкая Выше: код тестов, инфраструктура, поддержка
Повторный прогон Часы и дни Минуты, можно на каждый коммит
Слабое место Усталость на рутине Проверяет только запрограммированное; UI-тесты хрупкие

Это не выбор «или-или»: автоматизация берет стабильные повторяющиеся проверки, человек ищет то, что никто не описал в тест-кейсе.

Пирамида тестов

Пирамида тестов — модель распределения автотестов, которую популяризировал Майк Кон. Внизу много быстрых модульных тестов, посередине меньше интеграционных (API, сервисы), наверху немного медленных сквозных (end-to-end) через интерфейс.

Чем выше уровень, тем тест медленнее, дороже в поддержке и тем сложнее понять причину падения. Это ориентир, а не закон: если логика в основном во внешних интеграциях, команды осознанно делают больше интеграционных тестов (форма «трофей» или «ромб»). Антипаттерн — «рожок мороженого»: много ручных и UI-тестов при пустом нижнем слое.

Пример: модульный тест ловит ошибку на границе

Правило бизнеса: доставка бесплатная, если сумма заказа от 3000 рублей включительно, иначе стоит 300 рублей. Техника граничных значений подсказывает проверить значения по обе стороны границы: 2999, 3000 и 3001. Ниже полный рабочий пример на Python 3 со встроенным модулем unittest.

import unittest

FREE_FROM = 3000
PRICE = 300

def shipping_cost(total: int) -> int:
    if total < 0:
        raise ValueError("сумма не может быть отрицательной")
    return 0 if total >= FREE_FROM else PRICE

class ShippingTest(unittest.TestCase):
    def test_below_threshold(self):
        self.assertEqual(shipping_cost(2999), 300)

    def test_exactly_threshold(self):
        self.assertEqual(shipping_cost(3000), 0)

    def test_above_threshold(self):
        self.assertEqual(shipping_cost(3001), 0)

    def test_negative_total(self):
        with self.assertRaises(ValueError):
            shipping_cost(-1)

result = unittest.main(argv=["x"], exit=False, verbosity=0).result
print("провалено:", len(result.failures), "из", result.testsRun)

Запуск выводит OK и итог провалено: 0 из 4. Каждый тест проверяет одно утверждение, поэтому по имени упавшего теста видно, какое правило нарушено.

Теперь та же функция с типичной ошибкой: разработчик написал строгое сравнение >.

import unittest

def shipping_cost(total: int) -> int:
    return 0 if total > 3000 else 300  # ошибка: должно быть >=

class ShippingTest(unittest.TestCase):
    def test_exactly_threshold(self):
        self.assertEqual(shipping_cost(3000), 0)

    def test_above_threshold(self):
        self.assertEqual(shipping_cost(3001), 0)

result = unittest.main(argv=["x"], exit=False, verbosity=0).result
print("провалено:", len(result.failures), "из", result.testsRun)

Фактический результат: test_exactly_threshold падает с AssertionError: 300 != 0, итог провалено: 1 из 2. Тест с 3001 проходит: проверяя только «явно больше границы», дефект пропустили бы в релиз. Исправление — вернуть >=, как в первом примере.

Граница примера: модульный тест не скажет, правильно ли корзина передает сумму со скидкой, — это задача интеграционного уровня.

Как организован процесс

Типичный цикл выглядит так (в Agile-проектах шаги идут параллельно с разработкой, а не строго друг за другом):

  1. Анализ требований — что проверять и какие риски; неоднозначность в требованиях — тоже находка.
  2. Планирование — объем, среды, критерии окончания.
  3. Тест-дизайн — тест-кейсы или чек-листы по техникам тест-дизайна.
  4. Выполнение и баг-репорты — шаги воспроизведения, ожидаемый и фактический результат, окружение.
  5. Отчет — что проверено, что нет, какие риски остаются.

Выводы

  • Тестирование сравнивает фактическое поведение с ожидаемым на конечном наборе сценариев и не доказывает отсутствие ошибок.
  • Уровни (unit, интеграционное, системное, приемочное) отвечают на вопрос «что проверяем», виды (функциональное и нефункциональное) — «какое свойство».
  • Ручное и автоматизированное тестирование дополняют друг друга: автотесты для повторяющихся проверок, человек для исследования и удобства.
  • Пирамида тестов — ориентир держать основную массу быстрых тестов внизу, а не жесткое правило.
  • Техника граничных значений на простом примере находит дефект, который пропускают «очевидные» проверки.

Где применяется / связь с практикой

Освойте тему на практике

Все описанное — ежедневная работа QA-инженера: разбирать требования, проектировать проверки, заводить баг-репорты и переносить повторяющиеся сценарии в автотесты. Чтобы пройти этот путь системно, с практикой, посмотри программу курса «QA Engineer».

Освойте тему на практике

Попробовать формат можно на бесплатных открытых уроков Otus.

FAQ

Нужен ли тестировщик, если разработчики пишут unit-тесты?
Модульные тесты проверяют код по замыслу автора. Тестировщик смотрит на систему целиком и со стороны пользователя и находит другие проблемы: разрывы между модулями, неоднозначные требования, неудобные сценарии.

Что такое тест-кейс и чем он отличается от чек-листа?
Тест-кейс описывает шаги, входные данные и ожидаемый результат достаточно подробно, чтобы его выполнил другой человек. Чек-лист — короткий список того, что проверить, без детальных шагов; он быстрее в подготовке, но сильнее зависит от опыта исполнителя.

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

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)