Есть ли опыт написания тестов?

Ответ

Да, я активно пишу тесты для Flutter-приложений, покрывая различные уровни: unit, widget и интеграционные тесты.

1. Unit-тесты (пакет test): Использую для тестирования изолированной бизнес-логики, сервисов, репозиториев.

// Пример теста для класса Counter
import 'package:test/test.dart';

class Counter {
  int value = 0;
  void increment() => value++;
  void decrement() => value--;
}

void main() {
  group('Counter', () {
    test('value should start at 0', () {
      expect(Counter().value, 0);
    });

    test('value should be incremented', () {
      final counter = Counter();
      counter.increment();
      expect(counter.value, 1);
    });
  });
}

2. Widget-тесты (пакет flutter_test): Позволяют тестировать виджеты в изоляции, проверять их состояние и реакцию на взаимодействие.

// Пример теста для виджета
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('MyWidget displays a title and responds to tap', (WidgetTester tester) async {
    // Строим виджет
    await tester.pumpWidget(const MyWidget(title: 'T'));
    // Проверяем, что текст отобразился
    expect(find.text('T'), findsOneWidget);
    // Имитируем нажатие на кнопку
    await tester.tap(find.byType(ElevatedButton));
    await tester.pump(); // Триггерим перестроение
    // Проверяем результат
    expect(find.text('Clicked!'), findsOneWidget);
  });
}

3. Интеграционные тесты (пакет integration_test): Запускаю на реальном устройстве или эмуляторе для проверки полного сценария работы приложения.

// Пример интеграционного теста
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('Full app flow', (WidgetTester tester) async {
    app.main(); // Запускаем приложение
    await tester.pumpAndSettle(); // Ждем полной отрисовки
    // Выполняем последовательность действий
    await tester.tap(find.byIcon(Icons.add));
    await tester.pumpAndSettle();
    // Проверяем итоговое состояние UI
    expect(find.text('1'), findsOneWidget);
  });
}

Дополнительно: Для мокирования зависимостей (например, http.Client или репозиториев) использую mocktail или mockito. Тестовый конвейер настраиваю через flutter test для модульных/виджет-тестов и flutter test integration_test для интеграционных.

Ответ 18+ 🔞

А, тесты пишешь? Ну, молодец, конечно, это ж святое дело. Только вот, блядь, как послушаю некоторых — так у них либо тестов ноль ебать, либо они их пишут так, что потом сам чёрт ногу сломит. А нормально покрыть приложение — это ж целая наука, ёпта.

Смотри, я тебе по полочкам разложу, как у самого всё устроено, а то чувствую подозрение ебать, что многие эту тему через пень-колоду понимают.

1. Юнит-тесты (это типа test пакет) Вот это, блядь, основа основ. Берёшь какую-нибудь свою хитрожопую логику, какую-нибудь функцию, которая там скидки считает или пароль проверяет, и тестишь её в полной изоляции. Никаких виджетов, никакой базы данных рядом — чистая математика и условные операторы. Просто чтобы убедиться, что твой мозг, когда он код писал, не наебнулся в самый ответственный момент.

// Пример теста для класса Counter
import 'package:test/test.dart';

class Counter {
  int value = 0;
  void increment() => value++;
  void decrement() => value--;
}

void main() {
  group('Counter', () {
    test('value should start at 0', () {
      expect(Counter().value, 0);
    });

    test('value should be incremented', () {
      final counter = Counter();
      counter.increment();
      expect(counter.value, 1);
    });
  });
}

Вот смотри, тут всё просто, как три копейки. Создал счётчик, ткнул в него — он ноль. Ткнул кнопку increment — о чудо, стал единица. Если не стал — значит, ты где-то такой косяк в одну строчку запилил, что сам от себя охуеешь. А тест его сразу выловит, не дав тебе с этим говном в продакшен выкатиться.

2. Виджет-тесты (пакет flutter_test) А вот это уже поинтереснее. Тут ты проверяешь не просто логику, а то, как эта логика на экране отображается. Будто бы ты пользователь: нажимаешь на кнопки, скроллишь списки, смотришь, появляются ли нужные надписи. Только вместо пальцев у тебя WidgetTester, который всё это за тебя делает, быстрее и безотказнее.

// Пример теста для виджета
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('MyWidget displays a title and responds to tap', (WidgetTester tester) async {
    // Строим виджет
    await tester.pumpWidget(const MyWidget(title: 'T'));
    // Проверяем, что текст отобразился
    expect(find.text('T'), findsOneWidget);
    // Имитируем нажатие на кнопку
    await tester.tap(find.byType(ElevatedButton));
    await tester.pump(); // Триггерим перестроение
    // Проверяем результат
    expect(find.text('Clicked!'), findsOneWidget);
  });
}

Вот представь: запустил такой тест, а он тебе говорит — «слушай, а кнопка-то у тебя после нажатия надпись не меняет, хоть ты тресни». И ты такой: «ёбааать, точно, setState забыл вызвать!». Спасён, блядь, от позора. Виджет-тесты — они как преданный пёс: вовремя гавкнут, если что-то не так.

3. Интеграционные тесты (ну это integration_test) А это, дружок, уже тяжёлая артиллерия. Ты берёшь своё приложение целиком, как есть, со всеми этими навигациями, апишками и локальной базой, и запускаешь на реальном девайсе или эмуляторе. Цель одна — проверить, что весь этот зоопарк в связке работает, как задумано, а не разваливается при первом же чихе.

// Пример интеграционного теста
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('Full app flow', (WidgetTester tester) async {
    app.main(); // Запускаем приложение
    await tester.pumpAndSettle(); // Ждем полной отрисовки
    // Выполняем последовательность действий
    await tester.tap(find.byIcon(Icons.add));
    await tester.pumpAndSettle();
    // Проверяем итоговое состояние UI
    expect(find.text('1'), findsOneWidget);
  });
}

Вот это и есть полный цикл: открыл приложение, нашёл кнопку, нажал, подождал, пока всё устаканится, и проверил результат. Если здесь всё зелёное — можно спать спокойно. Ну, почти спокойно.

Ну и по мелочи: Чтобы не зависеть от интернета или какой-нибудь кривой базы, зависимости мокаешь. Mocktail — отличная штука, чтобы нарисовать себе виртуальный http.Client, который всегда отвечает тем, что тебе нужно для теста. А запускается вся эта красота командами flutter test (для юнитов и виджетов) и flutter test integration_test (для больших и страшных интеграционных сценариев).

Короче, суть в чём: без юнитов ты слепой, без виджет-тестов — полуслепой, а без интеграционных — просто надеешься на авось. А авось, как известно, хуй с горы — упадёт когда не ждёшь. Так что лучше покрывать всё, блядь, слоями, как многослойный бутерброд. И тогда жить станет проще, вот тебе честное слово.