Flutter поставляется с flutter_test из коробки, но написать хорошие тесты — не то же самое, что просто написать тесты. Типичная проблема Flutter-проектов: тесты есть, но они тестируют только «sunny day scenario», падают при малейшем изменении структуры, или держат внутри себя реальные HTTP-запросы. Один пропущенный баг в production обходится в 10 раз дороже, чем исправление на этапе тестирования. Мы накопили опыт на 20+ проектах и знаем, как избежать этих граблей. Гарантируем стабильное покрытие бизнес-логики без хрупких snapshot-тестов. Экономия времени на регрессии достигает 30%, а количество багов в продакшне сокращается вдвое — это подтверждают независимые исследования (Dart testing documentation).
Почему стоит инвестировать в unit-тесты для Flutter?
Unit-тесты сокращают время регрессионного тестирования на 30% и снижают количество багов в продакшне вдвое. Они быстро выполняются (миллисекунды) и не требуют эмулятора. Это первая линия обороны при рефакторинге: если логика сломалась, вы узнаете об этом до сборки приложения.
Стек для unit-тестов
| Инструмент | Назначение | Особенности |
|---|---|---|
flutter_test |
Базовый набор для Flutter | Встроен, содержит testWidgets, pumpWidget |
mocktail |
Создание моков | Не требует кодогенерации, нуль-безопасен |
bloc_test |
Тестирование BLoC/Cubit | Проверка последовательности состояний |
riverpod (ProviderContainer) |
Тестирование Riverpod | Изолированное окружение с переопределениями |
fake_async |
Контроль времени | Мгновенные тесты с Future.delayed, Timer |
Сравнение Mocktail и Mockito
| Критерий | Mocktail | Mockito |
|---|---|---|
| Кодогенерация | Нет | Требуется (build_runner) |
| Null-safety | Из коробки | Частично |
| any() для кастомных типов | Требуется registerFallbackValue | Требуется аргумент matcher |
| Поддержка Stream | Есть | Есть |
Mocktail в 2 раза быстрее настраивается — не нужно ждать кодогенерации.
Как тестировать BLoC с помощью blocTest?
BLoC — самая тестируемая архитектура в Flutter. blocTest делает assert последовательности состояний тривиальным:
blocTest<AuthCubit, AuthState>(
'emits [loading, authenticated] when login succeeds',
build: () {
when(() => mockAuthRepo.login(any(), any()))
.thenAnswer((_) async => User(id: '1', name: 'Test'));
return AuthCubit(authRepository: mockAuthRepo);
},
act: (cubit) => cubit.login('[email protected]', 'password'),
expect: () => [
const AuthState.loading(),
AuthState.authenticated(User(id: '1', name: 'Test')),
],
);
Если в act нужна задержка или асинхронность — await cubit.login(...) внутри act. blocTest в 3 раза сокращает код по сравнению с ручным подписыванием на стейт.
Как тестировать Riverpod-провайдеры?
ProviderContainer позволяет создать изолированное окружение с переопределёнными провайдерами:
test('userProvider returns user on success', () async {
final container = ProviderContainer(
overrides: [
userRepositoryProvider.overrideWithValue(MockUserRepository()),
],
);
addTearDown(container.dispose);
when(() => mockRepo.getUser('1')).thenAnswer((_) async => User(id: '1'));
final user = await container.read(userProvider('1').future);
expect(user.id, '1');
});
Тестирование Use Case и Repository
Use Case — чистая бизнес-логика без Flutter-зависимостей. Тестируется просто:
test('GetOrderUseCase applies discount when user is premium', () async {
when(() => mockOrderRepo.getOrder('order1'))
.thenAnswer((_) async => Order(price: 100, isPremium: true));
final result = await useCase.execute('order1');
expect(result.finalPrice, 85); // 15% скидка
});
Частая ошибка: тестировать Use Case через ViewModel/BLoC, а не напрямую. Это делает тест хрупким и медленным.
Как тестировать код с таймерами с помощью fake_async?
test('debounce search fires after 300ms', () {
fakeAsync((async) {
final controller = SearchController();
controller.query = 'flutter';
async.elapse(Duration(milliseconds: 200));
verifyNever(() => mockRepo.search(any()));
async.elapse(Duration(milliseconds: 100));
verify(() => mockRepo.search('flutter')).called(1);
});
});
fakeAsync позволяет управлять временем без реального sleep — тесты с debounce/throttle запускаются мгновенно.
Типичные ошибки при unit-тестировании Flutter
-
mocktailбезregisterFallbackValueдля кастомных типов —any()не работает с нестандартными классами без регистрации. - Тесты, которые мутируют глобальный state —
SharedPreferencesилиHiveв тестах нужно инициализировать черезSharedPreferences.setMockInitialValues({})перед каждым тестом. - Отсутствие
tearDown—ProviderContainer.dispose()иStreamController.close()забывают, и тесты текут памятью. - Слишком широкий охват: пытаются тестировать UI через unit-тесты вместо widget-тестов — это приводит к медленным и хрупким тестам.
Как настроить CI для автоматического прогона тестов? Пошаговая инструкция
- Добавьте в корень проекта файл
.github/workflows/flutter_test.yml. - Определите workflow на каждый PR:
- Checkout кода
- Установка Flutter (stable)
-
flutter pub get -
flutter analyze -
flutter test --coverage
- Для формирования отчёта о покрытии используйте
genhtml(установитеlcov). - Исключите generated-файлы с помощью пакета
remove_from_coverageили sed-фильтра. - Настройте порог покрытия (например, 80% строк кода) — при падении ниже workflow завершится ошибкой.
Процесс работы над тестами
- Анализ — изучаем архитектуру (BLoC/Riverpod/GetX) и определяем критические бизнес-логические цепочки.
- Проектирование — выбираем инструменты (mocktail, bloc_test), проектируем изолированные тестовые сценарии.
- Реализация — пишем тесты с покрытием не менее 80% бизнес-логики.
- Тест — прогоняем локально, проверяем, что тесты не зависят от порядка выполнения.
- Деплой — настраиваем CI, добавляем отчёт о покрытии в PR.
Что входит в работу
- Написание unit-тестов для бизнес-логики, BLoC/Cubit, Riverpod-провайдеров, репозиториев и use case.
- Использование Mocktail для моков — без кодогенерации.
- Настройка CI с автоматическим прогоном тестов и формированием отчёта о покрытии.
- Документирование подхода и код-ревью вашей команды.
- Гарантия стабильности тестов при рефакторинге.
Сроки: от 3 до 5 дней в зависимости от архитектуры (BLoC / Riverpod / GetX). Оценим ваш проект бесплатно — свяжитесь с нами для получения консультации по настройке тестирования.
Наш опыт
Мы занимаемся разработкой Flutter-приложений более 5 лет. За это время протестировали более 20 проектов: от стартапов до enterprise-решений. Используем актуальные версии Dart и пакетов, следим за официальной документацией по тестированию. Наши инженеры готовы обучить вашу команду культуре написания тестов.
Закажите бесплатную оценку вашего проекта — мы проанализируем текущее покрытие и предложим план улучшений.







