Разработка Integration-тестов для Flutter-приложения
Integration-тесты во Flutter запускаются на реальном устройстве или эмуляторе. Приложение стартует полностью: движок Dart, Flutter Framework, ваш код. WidgetTester здесь работает поверх настоящего рендеринга, а не синтетического. Именно поэтому integration-тесты находят то, что widget-тесты пропускают: проблемы с навигацией между реальными Route, тайминги анимаций на конкретном железе, поведение при низкой памяти.
Пакет integration_test
Современный способ — пакет integration_test из Flutter SDK (не сторонний, официальный с Flutter 2.5). Он заменил устаревший flutter_driver.
pubspec.yaml:
dev_dependencies:
integration_test:
sdk: flutter
flutter_test:
sdk: flutter
Структура директорий:
integration_test/
app_test.dart
test_driver/
integration_test.dart # только для запуска через flutter drive
Запуск на подключённом устройстве:
flutter test integration_test/app_test.dart
Запуск в Firebase Test Lab или BrowserStack — через flutter build apk --target integration_test/app_test.dart + загрузка APK.
Что тестируем и как
Типичный integration-тест покрывает полный пользовательский флоу: открыл приложение → авторизовался → перешёл на экран каталога → добавил товар в корзину → оформил заказ → увидел экран подтверждения.
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Full checkout flow', (tester) async {
app.main();
await tester.pumpAndSettle();
// Логин
await tester.enterText(find.byKey(Key('email')), '[email protected]');
await tester.enterText(find.byKey(Key('password')), 'password123');
await tester.tap(find.byKey(Key('login_btn')));
await tester.pumpAndSettle(timeout: Duration(seconds: 10));
expect(find.byKey(Key('home_screen')), findsOneWidget);
// Добавление в корзину
await tester.tap(find.byKey(Key('product_card_1')));
await tester.pumpAndSettle();
await tester.tap(find.byKey(Key('add_to_cart_btn')));
await tester.pumpAndSettle();
expect(find.text('1'), findsOneWidget); // badge на иконке корзины
});
}
pumpAndSettle(timeout: Duration(seconds: 10)) — таймаут нужен. Без него на слабом эмуляторе тест упадёт раньше, чем анимация навигации завершится.
Параллельный запуск
Один тест-файл — одна сессия устройства. Для параллельного прогона создаём несколько файлов и запускаем их через матрицу в CI. В GitHub Actions:
strategy:
matrix:
test-file: [auth_test.dart, checkout_test.dart, profile_test.dart]
steps:
- run: flutter test integration_test/${{ matrix.test-file }}
Каждый job получает свой эмулятор. AVD Manager через reactivecircus/android-emulator-runner@v2 запускает их параллельно.
Работа с реальными сетевыми запросами
Здесь главный выбор: тестировать против реального бэкенда или мокировать сеть.
Реальный бэкенд — нужна тестовая среда (staging). Данные должны быть предсказуемы: фиксированные тестовые аккаунты, детерминированные ответы. Сетевые ошибки делают тесты нестабильными.
Мок-сервер — mockito/mocktail на уровне репозитория, или локальный HTTP-сервер через пакет shelf. Второй вариант ближе к реальности: приложение делает настоящий HTTP-запрос, но на localhost:8080 вместо api.production.com. Переключение через переменную окружения или --dart-define=API_BASE_URL=http://localhost:8080.
Нестабильность integration-тестов
Flaky-тесты в integration_test — распространённая проблема. Основные причины:
Анимации. pumpAndSettle() ждёт их завершения, но если в приложении есть бесконечная анимация (индикатор загрузки, lottie-анимация в фоне), pumpAndSettle() зависнет. Решение: tester.pump(Duration(milliseconds: 500)) с явным ожиданием конкретного виджета через waitFor.
Клавиатура. После enterText() системная клавиатура может перекрывать кнопку. Прячем: await tester.testTextInput.receiveAction(TextInputAction.done) или FocusManager.instance.primaryFocus?.unfocus() перед tap().
Таймаут теста. По умолчанию integration-тест завершается через 10 минут. Для длинных флоу увеличиваем: @Timeout(Duration(minutes: 5)) над testWidgets.
Сроки
3–5 дней для написания integration-тестов к существующему приложению. Сложный флоу с авторизацией, платежами и глубокой навигацией — ближе к 5 дням. Простые линейные сценарии — 3 дня. Стоимость рассчитывается после аудита архитектуры приложения.







