Урок по треку Flutter и Dart 6 мин

Тестирование: unit, widget, integration

Unit, widget и integration-тесты, pump, моки и golden-тесты.

Во Flutter три уровня тестов. Чем выше уровень, тем ближе к пользователю и тем медленнее и дороже тест. Стандартный совет: много unit-тестов, достаточно widget-тестов и немного integration-тестов на ключевые сценарии.

УровеньЧто проверяетГде выполняетсяСкорость
Unitфункцию, класс, блокDart VMмгновенно
Widgetодин виджет или экрантестовое окружение без устройствабыстро
Integrationприложение целикомэмулятор или устройствомедленно

Unit-тесты

Пакет test (во Flutter-проекте flutter_test, который его включает):

void main() {
  group('Cart', () {
    late Cart cart;
    setUp(() => cart = Cart());

    test('сумма считается с учётом количества', () {
      cart.add(const Item(price: 100), qty: 2);
      expect(cart.total, 200);
    });
  });
}

Зависимости (API, база) подменяют: моками через mocktail или mockito (последний требует генерации кода) или простыми фейковыми классами. Для BLoC есть blocTest: задаёшь начальное состояние, действие и ожидаемую последовательность состояний.

Widget-тесты

testWidgets поднимает виджет без устройства. Время в тесте виртуальное: кадры и таймеры двигаются только когда вы их «прокачиваете».

testWidgets('счётчик растёт по нажатию', (tester) async {
  await tester.pumpWidget(const MaterialApp(home: CounterPage()));
  expect(find.text('0'), findsOneWidget);

  await tester.tap(find.byIcon(Icons.add));
  await tester.pump();               // один кадр после setState

  expect(find.text('1'), findsOneWidget);
});
  • pumpWidget строит виджет. Почти всегда его оборачивают в MaterialApp и провайдеры, иначе нет темы, Directionality, навигатора.
  • pump() отрисовывает один кадр, pump(Duration) продвигает время.
  • pumpAndSettle() крутит кадры, пока анимации не закончатся. С бесконечной анимацией (CircularProgressIndicator, repeat()) он упадёт по тайм-ауту: там нужен pump с длительностью.
  • Поиск: find.text, find.byType, find.byKey, find.byIcon. Проверки: findsOneWidget, findsNothing, findsNWidgets(n).
  • Действия: tap, enterText, drag, longPress.

Ключи (Key('submit')) стабильнее текста: текст меняется при локализации.

Golden-тесты

Сравнивают картинку виджета с эталонным PNG: await expectLater(find.byType(Card), matchesGoldenFile('card.png'));. Эталоны обновляют флагом flutter test --update-goldens. Минус: шрифты и сглаживание отличаются между ОС, поэтому golden-тесты гоняют в одном окружении, например в CI на Linux.

Integration-тесты

Пакет integration_test запускает настоящее приложение на эмуляторе или устройстве тем же API WidgetTester. Подходят для критичных сценариев: вход, оплата, онбординг. Сторонние инструменты вроде Patrol добавляют работу с нативными диалогами (разрешения, уведомления), которые сам integration_test не видит.

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

  • Зависимости передаются снаружи (конструктор, провайдер, get_it), а не создаются внутри виджета.
  • Логика в блоке или нотифаере, а не в build.
  • Время и случайность абстрагированы: clock, свой генератор.

Что спросят на собесе

  • Виды тестов во Flutter, чем отличаются, пропорции.
  • pump против pumpAndSettle, почему тест зависает на индикаторе загрузки.
  • Как замокать зависимости, mocktail против mockito.
  • Что такое golden-тесты и их подводные камни.
  • Как сделать экран тестируемым.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.