Функциональное и нефункциональное

Продукт можно провалить двумя способами: он делает не то, что нужно, — или делает ровно то, что нужно, но так, что пользоваться этим невозможно.

Функциональное тестирование отвечает на вопрос «что система делает». Нефункциональное — на вопрос «как она это делает».

Представьте банковское приложение. Перевод уходит на правильный счёт, сумма списывается копейка в копейку, история операций сходится. С функциональной точки зрения — идеально. А теперь детали: перевод подтверждается 40 секунд, кнопка «Подтвердить» на маленьком экране уезжает под кромку, сессия не истекает никогда, а в логах лежит номер карты целиком. Ни один функциональный тест этого не заметит: результат-то верный.

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

Функциональное: проверяем результат

Источник проверок здесь — требования: техническое задание, user story с критериями приёмки, макеты, API-контракт. Плюс здравый смысл, потому что половина реальных требований нигде не записана.

Возьмём форму перевода денег: поле «сумма», поле «получатель», кнопка. Функциональные проверки распадаются на три группы.

ГруппаЧто проверяемПример
Позитивныесистема делает то, что должна, на корректных данныхперевод 500 ₽ на существующий счёт проходит, баланс уменьшился на 500
Негативныесистема корректно отказывает на некорректных данныхперевод 500 ₽ при балансе 100 ₽ отклонён, деньги не списаны, показана внятная ошибка
Граничныеповедение на краях диапазонасумма 0, сумма 0.01, сумма ровно равная балансу, сумма на 1 копейку больше лимита

Оформляется это обычным тест-кейсом — шаги, данные, ожидаемый результат:

ID: TR-014
Название: Перевод суммы, превышающей баланс
Предусловие: пользователь авторизован, баланс = 100.00 RUB
Шаги:
  1. Открыть «Переводы» → «По номеру счёта»
  2. Ввести счёт 40817810099910004312
  3. Ввести сумму 500.00
  4. Нажать «Перевести»
Ожидаемый результат:
  - перевод отклонён;
  - показано сообщение «Недостаточно средств. Доступно 100,00 ₽»;
  - баланс остался 100.00 RUB;
  - операция НЕ появилась в истории.

Заметьте: ожидаемый результат — это не только «показалась ошибка». Проверяется ещё и то, что не произошло побочных эффектов. Половина боевых багов живёт именно там: сообщение об ошибке пользователь видит, а деньги всё равно списались.

Нефункциональное: четыре главных семейства

Нефункциональных видов насчитывают десятки, но на реальном проекте новичок почти всегда сталкивается с четырьмя.

Производительность

Вопрос: сколько ждёт пользователь и сколько выдерживает система. Внутри — несколько разных проверок, которые часто путают:

  • Нагрузочное — держим ожидаемую нагрузку (200 одновременных пользователей) и смотрим время ответа.
  • Стрессовое — давим сверх ожидаемого, пока не сломается, и смотрим как сломается: аккуратно вернёт 503 или ляжет с потерей данных.
  • Стабильности (soak) — держим среднюю нагрузку 8–12 часов и ловим утечки памяти: система работает, но к вечеру отвечает вдвое медленнее.
  • Объёмное — не пользователей много, а данных: список из 200 000 записей, файл на 2 ГБ.

Ключевая величина — не «среднее время ответа», а перцентиль. Среднее в 300 мс прекрасно уживается с тем, что каждый двадцатый запрос идёт 9 секунд. Поэтому требования пишут так: p95 < 800 мс при 200 RPS. Инструменты: JMeter, k6, Locust.

Безопасность

Полноценный пентест делают специалисты, но базовый слой проверок — работа обычного QA. Минимум, который проходится руками и браузерным DevTools:

  • Подставить в URL чужой идентификатор: /orders/1043 вместо своего /orders/1042. Если чужой заказ открылся — это IDOR, одна из самых частых дыр.
  • Скормить в поле поиска ' OR 1=1 -- и посмотреть, не изменилась ли выдача (SQL-инъекция).
  • Вставить в комментарий текст с HTML-тегом скрипта и проверить, экранируется ли он (XSS).
  • Ввести неверный пароль 20 раз подряд: есть ли блокировка или капча.
  • Разлогиниться и повторить старый запрос со старым токеном — токен должен умереть.
  • Посмотреть, что уходит в логи и в аналитику: пароли и номера карт там появляться не должны.

Ориентир на список OWASP Top 10 — это открытый перечень самых распространённых веб-уязвимостей, его удобно использовать как чек-лист.

Юзабилити

Здесь важно сразу отделить «мне не нравится цвет» от настоящей проблемы удобства. Юзабилити-баг формулируется через цель пользователя и препятствие на пути к ней:

  • Сколько шагов до цели: оформление заказа занимает 9 экранов, из них 3 — необязательные.
  • Понятность ошибки: Error 500: transaction_failed вместо «Банк отклонил платёж. Попробуйте другую карту».
  • Обратная связь: пользователь жмёт «Оплатить», ничего не происходит 5 секунд, он жмёт ещё раз — и оплачивает дважды.
  • Соответствие ожиданиям: кнопка «Назад» в браузере уносит из середины оформления заказа на главную и стирает корзину.

Настоящее юзабилити-исследование — это наблюдение за живыми людьми. Классическая практика: посадить пять человек, дать задачу «купите билет в Казань на завтра», молчать и записывать, где они запнулись. Пятерых обычно хватает, чтобы найти большинство грубых проблем.

Совместимость

Одно и то же приложение по-разному ведёт себя в Safari на iPhone и в Chrome на Windows. Проверять «все устройства мира» невозможно, поэтому строят матрицу приоритетов, опираясь на аналитику реального трафика:

КонфигурацияДоля трафикаГлубина проверки
Chrome, Windows, 1920×108046%полный регресс
Safari, iOS, 390×84427%полный регресс
Chrome, Android, 412×91518%полный регресс
Firefox, Windows5%smoke: вход, поиск, оплата
Остальное4%чиним по жалобам

Совместимость — это не только браузеры. Ещё версии ОС, версии внешнего API, версия базы данных после апгрейда, старые версии мобильного приложения, которые пользователи не обновили и которые продолжают ходить в ваш новый бэкенд.

Как это работает

Нефункциональные требования (их называют NFR, non-functional requirements) живут в тех же документах, что и обычные: в ТЗ, в критериях приёмки, в SLA — соглашении об уровне обслуживания, где записано, что и с какой скоростью система обязана делать.

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

Хорошее нефункциональное требование содержит четыре части: субъект, условие, метрику и порог.

Плохо:  «Система должна работать быстро и надёжно.»

Хорошо: «При 200 одновременных пользователях 95% запросов к /api/search
         возвращают ответ быстрее 800 мс; доля ошибок 5xx — не выше 0,1%.
         Замер: k6, 15 минут, окружение stage с продовым объёмом данных.»

Ритм у этих проверок тоже разный. Функциональные тесты гоняют на каждый релиз. Нагрузочные — перед распродажей и после серьёзных изменений в базе. Аудит безопасности — по расписанию, раз в квартал или полгода. Юзабилити-исследование — на этапе прототипа, пока переделывать дёшево.

Частые ошибки

  • Ждать, что NFR принесут на блюде. Не принесут. Если в требованиях нет цифр — идите к аналитику и продакту и добывайте их до старта тестирования, а не после жалоб с прода.
  • Мерить производительность на своём ноутбуке. Через домашний Wi-Fi, на базе из 50 записей, с открытым Docker и десятком вкладок. Такие цифры не значат ничего: нужен стенд, похожий на боевой, и продовый объём данных.
  • Смотреть на среднее время ответа. Оно прячет самые больные хвосты. Всегда p95 и p99.
  • Заводить юзабилити-баг как «неудобно». Такой тикет закроют. Пишите: цель пользователя, где он застрял, сколько людей застряло, чем это грозит бизнесу.
  • Считать безопасность чужой зоной. Подмена id в URL и отсутствие лимита на попытки входа проверяются за пять минут и находятся куда чаще, чем кажется.
  • Тестировать совместимость «везде». Без матрицы приоритетов вы потратите неделю на браузер, которым пользуется 0,3% аудитории, и не откроете сайт на iPhone.
  • Гонять нагрузочный тест по проду. Если очень надо — только по согласованию, в ночное окно и с планом отката. Иначе тест сам станет инцидентом.

Итоги

  • Функциональное тестирование проверяет что система делает, нефункциональное — как: скорость, устойчивость, защищённость, удобство, поведение на разном железе.
  • В функциональной проверке ожидаемый результат включает и побочные эффекты, которых быть не должно.
  • Четыре семейства, которые встретятся первыми: производительность, безопасность, юзабилити, совместимость.
  • Требование без метрики и порога непроверяемо. «Быстро» — не требование, «p95 меньше 800 мс при 200 RPS» — требование.
  • Производительность меряют перцентилями, безопасность начинают с OWASP Top 10, юзабилити — с наблюдения за живыми людьми, совместимость — с матрицы по реальному трафику.
Проверьте себя
1. Приложение корректно переводит деньги, но подтверждение операции занимает 40 секунд. К какому виду тестирования относится проверка, которая поймает эту проблему?
AФункциональному — результат перевода ведь важен
BНефункциональному, а именно тестированию производительности
CПриёмочному — решает заказчик
DЭто не дефект: операция в итоге проходит успешно
2. Какое из требований можно проверить, не задавая аналитику дополнительных вопросов?
AСистема должна быть удобной для пользователя
BСтраницы должны открываться быстро
CПри 200 одновременных пользователях 95% запросов к /api/search отвечают быстрее 800 мс
DПриложение должно работать на всех современных устройствах