Перейти к основному содержимому

Тестирование Flutter-приложений

Три уровня, которые даёт Flutter, чем хорош каждый из них и как писать тесты, которые падают, когда сломан продукт, а не когда переехал widget.

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

Что нужно понимать

  • Что этот тест поймает такого, чего не поймает ничто другое
  • Проверяет он поведение или реализацию
  • Что подменено и насколько подмена всё ещё похожа на реальность
  • Почему он медленный, если он медленный
  • Понятно ли из упавшего теста, что именно сломалось

Ключевые темы

Уровни

  • Unit-тесты: чистая логика, миллисекунды, основная часть набора
  • Widget-тесты: один widget или экран с настоящим деревом, без устройства
  • Интеграционные тесты: приложение целиком на устройстве или эмуляторе
  • Golden-тесты для отрисовки и цена их поддержки

Widget-тесты

  • pumpWidget, pump и pumpAndSettle — и что каждый из них делает на самом деле
  • Finders и почему искать по семантике лучше, чем по типу
  • Время: fake async, таймеры и анимации, которые никогда не заканчиваются
  • Подмена зависимостей по правильному шву
  • Загрузка, ошибка и пустой результат как полноценные случаи, а не как довесок

Как сделать код тестируемым

  • Внедрять зависимости, а не тянуться к глобальным объектам
  • Держать логику вне widget'ов — тогда большей её части widget-тесты не нужны
  • Швы для сети, хранилища, времени и случайности
  • Fake вместо mock там, где fake проще

Набор тестов

  • Скорость: медленный набор просто перестают запускать
  • Нестабильный тест — это ошибка, а не свойство природы
  • Покрытие как подсказка, но никогда как цель
  • Что гоняется на каждый коммит, а что — ночью

Уровни

УровеньКак это выглядит
JuniorПишет unit-тесты для функций и следует принятым в проекте образцам widget-тестов.
MiddleВыбирает подходящий уровень, подменяет зависимости по разумным швам, проверяет состояния, а не только удачный сценарий, чинит нестабильные тесты.
SeniorВыстраивает быстрый набор тестов, которому можно верить, держит тесты на уровне поведения, чтобы рефакторинг оставался дешёвым, и понимает, что тестировать не надо.

Практика

Для начала

  • Проверьте состояния Напишите widget-тесты на состояния загрузки, пустого результата и ошибки, а не только на успешное.

  • Уберите один pumpAndSettle Найдите тот, что зависает на повторяющейся анимации, и замените его явными вызовами pump.

  • Подмените часы Протестируйте что-нибудь зависящее от времени, не прибегая к sleep.

Дальше

  • Тестируйте поведение, а не структуру Возьмите тест, который ломается на каждом рефакторинге, и перепишите его на проверку поведения.

  • Почините нестабильный тест Найдите тест, который падает через раз, разберитесь, где на самом деле гонка, и устраните причину.

  • Добавьте один интеграционный тест Покройте самый важный для вас сценарий целиком, от начала до конца, и запускайте его в CI.

Проверьте себя

  • Какие из ваших тестов пройдут, даже если функциональность сломана?
  • Какие падают при каждом рефакторинге, который не меняет поведение?
  • Сколько идёт ваш набор тестов и когда это в последний раз кого-нибудь волновало?
  • Что подменено в ваших widget-тестах и соответствует ли подмена реальности до сих пор?
  • Поломка какого сценария обойдётся дороже всего и проверен ли он целиком, от начала до конца?
  • Чего вы сознательно не тестируете и почему?

Материалы

  • Testing Flutter apps — официальный обзор трёх уровней и того, когда какой уместен.
  • Widget testing introduction — практическая отправная точка: finders и всё семейство pump.
  • flutter_test API — стоит один раз пролистать: в WidgetTester заметно больше, чем используют туториалы.
  • Test Desiderata — Кент Бек о двенадцати свойствах хорошего теста. Самый внятный набор критериев, чтобы решить, окупается ли тест.
  • mocktail — подмена зависимостей с null safety и без кодогенерации, и документация, которая честно говорит, когда fake лучше mock.