Разработка Widget-тестов для Flutter-приложения

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Разработка Widget-тестов для Flutter-приложения
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Разработка Widget-тестов для Flutter-приложения

Widget-тесты во Flutter занимают ту нишу, которую в мобильной разработке обычно заполняет либо юнит-тест (слишком мелко), либо интеграционный тест (слишком медленно). Рендеринг виджетов, навигация по нажатию кнопки, отображение данных из провайдера — всё это проверяется без запуска на устройстве, за секунды, через WidgetTester и синтетический движок рендеринга.

Типичные проблемы, с которых начинают

Самая частая ошибка: пытаться написать widget-тест для виджета, который завязан на реальный BuildContext — например, читает Theme.of(context) и падает с No MaterialApp found или No Directionality widget. Решение — всегда оборачивать тестируемый виджет в MaterialApp или минимальный Directionality:

await tester.pumpWidget(
  MaterialApp(home: MyWidget()),
);

Вторая проблема — RenderFlex overflowed в тестах, которого не было в дебаггере. Это значит размер WidgetTester по умолчанию (800×600) не совпадает с реальным устройством. Исправляем через tester.binding.window.physicalSizeTestValue или tester.view.physicalSize = const Size(390, 844) (в актуальном Flutter 3.x API).

Архитектура виджет-тестов

Структура тест-файла соответствует структуре виджета: test/widgets/ зеркалит lib/widgets/. Каждый тест-файл — один виджет или один экран.

group('LoginScreen', () {
  testWidgets('shows error when email is invalid', (tester) async {
    await tester.pumpWidget(MaterialApp(home: LoginScreen()));

    await tester.enterText(find.byKey(Key('email_field')), 'not-an-email');
    await tester.tap(find.byKey(Key('submit_button')));
    await tester.pump(); // синхронный кадр

    expect(find.text('Введите корректный email'), findsOneWidget);
  });
});

pump() vs pumpAndSettle() — критичное различие. pump() рисует один кадр. pumpAndSettle() крутит кадры до тех пор, пока не останется pending-анимаций. На виджетах с бесконечными анимациями (AnimatedBuilder с repeat: true) pumpAndSettle() зависнет навсегда — используем pump(Duration(seconds: 2)).

Моки провайдеров и зависимостей

Widget-тест без мокирования зависимостей — это не виджет-тест, а интеграционный. Для Riverpod переопределяем провайдеры через ProviderScope.overrides:

await tester.pumpWidget(
  ProviderScope(
    overrides: [
      userProvider.overrideWithValue(AsyncValue.data(mockUser)),
    ],
    child: MaterialApp(home: ProfileScreen()),
  ),
);

Для BLoCBlocProvider с мок-блоком через mocktail или mockito. Для GetIt — регистрируем мок-реализацию до теста и сбрасываем после через tearDown.

Golden Tests

Golden Tests — отдельная категория. Виджет рендерится, скриншот сравнивается с эталонным .png в test/goldens/. При первом запуске эталон генерируется (flutter test --update-goldens), при последующих — любое пиксельное расхождение ломает тест.

testWidgets('PrimaryButton golden', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      home: Center(child: PrimaryButton(label: 'Сохранить')),
    ),
  );
  await expectLater(
    find.byType(PrimaryButton),
    matchesGoldenFile('goldens/primary_button.png'),
  );
});

Проблема с Golden Tests: они платформо-зависимы. Шрифты, сглаживание, рендеринг теней — всё различается на macOS, Linux и Windows CI. Решение — запускать golden-тесты только на определённой платформе (canvaskit рендерер в CI через flutter test --platform chrome для web, или фиксированный Docker-образ с Ubuntu для мобильных golden'ов).

Пакет golden_toolkit (pub.dev) добавляет loadAppFonts(), что устраняет прямоугольники вместо текста в эталонах.

Async и Future в тестах

Если виджет запускает Future при инициализации (например, FutureBuilder + HTTP-запрос), в тесте нужно контролировать завершение этого future. Без мока сетевой вызов либо упадёт, либо зависнет.

when(() => mockApiService.getUser()).thenAnswer((_) async => mockUser);

await tester.pumpWidget(/* ... */);
await tester.pump(); // запускает FutureBuilder
await tester.pump(Duration.zero); // ждём завершения Future

Fake вместо Mock — когда поведение сложное. Реализуем FakeAuthService extends AuthService, переопределяем нужные методы — чище, чем stub каждого вызова.

Что входит в работу

  • Написание widget-тестов для всех ключевых экранов и компонентов
  • Настройка Golden Tests с корректной платформой для CI
  • Мокирование провайдеров (Riverpod, BLoC, Provider, GetIt)
  • Покрытие edge-кейсов: пустые состояния, ошибки, loading
  • Настройка запуска в CI с отчётом о покрытии

Сроки

3–5 дней для проекта со стандартным набором экранов (10–20 виджетов). Golden Tests на весь UI компонентной библиотеки — отдельная оценка. Стоимость рассчитывается индивидуально.