Ответ
Пирамида тестирования — это концепция, описывающая оптимальное соотношение различных типов тестов в проекте для обеспечения надежности и эффективности. В Flutter она выглядит так:
-
Unit-тесты (основание) — тестируют отдельные функции, классы или методы в изоляции. Они самые быстрые и многочисленные.
// Пример unit-теста для простого счетчика test('Counter value should be incremented', () { final counter = Counter(); counter.increment(); expect(counter.value, 1); });Для них мы используем пакет
test. Они не требуют запуска Flutter-движка. -
Widget-тесты (середина) — тестируют отдельные виджеты в изолированной среде. Они медленнее unit-тестов, но проверяют рендеринг и взаимодействие.
// Пример widget-теста testWidgets('MyWidget has a title and message', (WidgetTester tester) async { await tester.pumpWidget(const MyWidget(title: 'T', message: 'M')); expect(find.text('T'), findsOneWidget); expect(find.text('M'), findsOneWidget); });Для них используется
flutter_test. -
Интеграционные (E2E) тесты (верхушка) — тестируют полное приложение на реальном устройстве или эмуляторе, имитируя действия пользователя. Они самые медленные, хрупкие и их должно быть меньше всего.
// Пример интеграционного теста с integration_test testWidgets('Tap on floating action button, verify counter', (tester) async { app.main(); await tester.pumpAndSettle(); expect(find.text('0'), findsOneWidget); await tester.tap(find.byType(FloatingActionButton)); await tester.pumpAndSettle(); expect(find.text('1'), findsOneWidget); });
Принцип: Создаем широкое основание из быстрых и надежных unit-тестов, поддерживаем его меньшим количеством widget-тестов и завершаем пирамиду минимальным набором интеграционных тестов для критических пользовательских сценариев. Это позволяет быстро находить ошибки и поддерживать высокую скорость разработки.
Ответ 18+ 🔞
Слушай, а ведь эта ваша пирамида тестирования — она как будто про жизнь написана, ёпта. Снизу, где всё стабильно и быстро — это как будто зарплата пришла, можно unit-тесты гонять пачками. А наверху, где один ебёж на реальном устройстве — это как поход в ЖЭК, один раз сходил, и хватит на месяц, волнение ебать.
Ну, короче, смотри. Вся эта хрень строится на простой идее: чтобы не ебаться с дебагом неделями, нужно тесты писать. Но если все тесты будут тяжёлыми и медленными, то ты будешь ждать их завершения дольше, чем бабка в очереди за пенсией. Поэтому всё делят на три слоя, как торт, только невкусный.
1. Unit-тесты (это основание, их дохуя) Это как проверять, работает ли твой чайник, не подключая его к розетке. Берёшь одну функцию, один класс, даёшь ему на вход данные и смотришь, что он выплюнет. Быстро, дёшево, их можно наклепать овердохуища. Они не требуют запуска всего Flutter-движка, так что летают.
// Проверяем, что счётчик не мухлюет
test('Counter value should be incremented', () {
final counter = Counter();
counter.increment();
expect(counter.value, 1); // Если тут 0, то всё, пизда рулю
});
Вот это и есть фундамент. Если он сыпется — дальше можно даже не смотреть, чувак.
2. Widget-тесты (середина пирамиды) Тут уже интереснее. Это как если бы ты собрал шкаф из Икеи, но проверяешь не всё сразу, а только одну полку: влезает ли она, не шатается ли. Запускается изолированная среда Flutter, чтобы проверить, правильно ли виджет рисуется и реагирует на тапы.
testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));
expect(find.text('T'), findsOneWidget); // Ищем текст 'T'
expect(find.text('M'), findsOneWidget); // Ищем текст 'M'
// Если не нашёл — виджет кривой, надо пересобирать
});
Они уже медленнее, чем unit-тесты, и их должно быть меньше. Но без них тоже никуда — а то вдруг кнопка не нажимается, ядрёна вошь.
3. Интеграционные (E2E) тесты (самая верхушка, их мало) А вот это уже полный пиздец, извини за выражение. Представь, ты сажаешь свою бабушку (которая в телефоне только звонить умеет) перед твоим приложением и говоришь: «Вот, протестируй». Запускается ВСЁ приложение на реальном устройстве или эмуляторе, и скрипт тыкает везде, как пьяный пользователь.
testWidgets('Tap on floating action button, verify counter', (tester) async {
app.main(); // Запускаем всё приложение, ёперный театр
await tester.pumpAndSettle(); // Ждём, пока всё устаканится
expect(find.text('0'), findsOneWidget); // Изначально должен быть 0
await tester.tap(find.byType(FloatingActionButton)); // Тыкаем на кнопку
await tester.pumpAndSettle(); // Снова ждём
expect(find.text('1'), findsOneWidget); // Должно стать 1
});
Они самые медленные, самые хрупкие (сегодня работает, а завтра нет, потому что облако упало), и их должно быть раз-два и обчёлся. Только для самых критичных сценариев, типа «купить товар» или «войти в аккаунт». Если они падают — это уже серьёзный звонок, терпения ноль ебать.
Так в чём же соль, спросишь ты? А соль в балансе. Если ты будешь писать только интеграционные тесты, то будешь ждать их результатов полдня, и доверия к ним ебать ноль, потому что они постоянно врут из-за всякой ерунды. Если будешь писать только unit-тесты — то можешь пропустить, как вся эта красота разваливается при сборке воедино.
Идеальная картина: внизу у тебя куча быстрых unit-тестов, которые ловят косяки в логике сразу. Над ними слой widget-тестов, которые проверяют, что интерфейс не рассыпается. И на самом верху — несколько жирных E2E-тестов, которые гарантируют, что главные фичи хоть как-то работают. Так ты и скорость разработки сохраняешь, и не выпускаешь в продакшн откровенную дичь. Всё просто, как три копейки.
Видео-ответы
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶