Урок 25 из 25 · Месяц 6. Чат и публикация приложений

Тестовое задание для отбора в Geeks Pro

Содержание урока

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

Что такое тестовое задание и что в нём проверяют

Тестовое задание — это мини-проект на несколько дней: например, приложение со списком данных из API, детальным экраном и авторизацией. По нему видно не только «работает или нет», но и то, как вы думаете: как разложили код, как обработали ошибки, насколько аккуратно оформили проект.

Почему важно сделать самому, без ИИ

ИИ-ассистенты — полезный инструмент в работе. Но тестовое задание проверяет ваши навыки, и сделать его самостоятельно важно по нескольким причинам:

  • Вас спросят о каждой строке. После тестового обычно идёт обсуждение: «Почему здесь Bloc, а не Cubit?», «Что будет, если сервер вернёт 500?». Если код писали не вы, это видно за пару минут.
  • Вы узнаете свои пробелы. Застряли на пагинации — значит, именно её надо повторить. ИИ скроет этот пробел, и он всплывёт уже на работе.
  • Честная оценка — в ваших интересах. Если вас отберут по чужому коду, дальше будет тяжело: задачи станут сложнее, а базы нет.
  • Это тренировка реальной работы. Разобраться в документации, найти ошибку, принять решение — главный навык разработчика.

Пользоваться официальной документацией (docs.flutter.dev, dart.dev, pub.dev) и своими конспектами курса — нормально: так работают все разработчики.

Как подходить к тестовому: пошагово

Шаг 1. Внимательно прочитайте задание

Прочитайте дважды и выпишите требования списком. Разделите их на обязательные и дополнительные («будет плюсом»). Если что-то непонятно — задайте вопрос сразу, а не в последний день. Умение задавать уточняющие вопросы — тоже плюс.

Шаг 2. Спланируйте

Прежде чем писать код, набросайте план: экраны, сущности, слои, пакеты.

Экраны:   Список товаров → Детальная страница → Избранное
Сущности: Product (id, title, price, image, description)
Слои:     data (ProductModel, ProductRemoteDataSource, ProductRepositoryImpl)
          domain (Product, ProductRepository, GetProductsUseCase)
          presentation (ProductsBloc, ProductsPage, ProductDetailsPage)
Пакеты:   flutter_bloc, equatable, dio, get_it, json_serializable

Распределите время: например, при сроке в 5 дней — день на каркас и сеть, два на основные экраны, день на дополнительные задачи, день на тесты, README и проверку.

Шаг 3. Сначала — работающий минимум

Сделайте обязательную часть целиком и просто, пусть без красоты. Рабочее приложение без «плюсов» лучше, чем красивое, но недоделанное. Только потом — дополнительные задачи и полировка.

Шаг 4. Коммитьте понятно и часто

История Git показывает, как вы работали. Один коммит «done» на весь проект выглядит подозрительно.

git commit -m "Add project structure and DI setup"
git commit -m "Add Product model and remote data source"
git commit -m "Add ProductsBloc with loading and error states"
git commit -m "Add pagination to products list"
git commit -m "Add README with setup instructions"

Шаг 5. Проверьте перед сдачей

flutter analyze        # нет ошибок и предупреждений
dart format .          # код отформатирован
flutter test           # тесты проходят
flutter run --release  # приложение работает в release

Склонируйте свой репозиторий в новую папку и запустите по README — так вы проверите проект глазами проверяющего.

Пример тестового задания

Ниже — учебный пример, похожий на типичные тестовые. Попробуйте выполнить его как тренировку.

Задание: приложение «Каталог товаров»

Обязательно:

  1. Экран списка товаров из открытого API: картинка, название, цена.
  2. Пагинация: подгрузка следующей страницы при прокрутке к концу.
  3. Детальный экран товара.
  4. Состояния загрузки, ошибки (с кнопкой «Повторить») и пустого списка.
  5. Clean Architecture и Bloc, зависимости через GetIt.
  6. README с инструкцией по запуску.

Будет плюсом:

  • поиск с задержкой (debounce);
  • избранное с сохранением между запусками;
  • светлая и тёмная темы;
  • unit-тесты для Bloc и репозитория.

Срок: 5 дней. Сдача: ссылка на публичный репозиторий.

Пример: Bloc с пагинацией

Основа решения — Bloc, который умеет загрузить первую страницу и догрузить следующие:

class ProductsBloc extends Bloc<ProductsEvent, ProductsState> {
  ProductsBloc(this._getProducts) : super(const ProductsState()) {
    on<ProductsFetched>(_onFetched, transformer: droppable());
  }

  final GetProductsUseCase _getProducts;
  static const _pageSize = 20;

  Future<void> _onFetched(ProductsFetched event, Emitter<ProductsState> emit) async {
    if (state.hasReachedMax) return;
    try {
      final items = await _getProducts(page: state.page, limit: _pageSize);
      emit(state.copyWith(
        status: ProductsStatus.success,
        products: [...state.products, ...items],
        page: state.page + 1,
        hasReachedMax: items.length < _pageSize,
      ));
    } catch (e) {
      emit(state.copyWith(status: ProductsStatus.failure, error: e.toString()));
    }
  }
}
  • droppable() из пакета bloc_concurrency игнорирует новые ProductsFetched, пока идёт загрузка, — нет двойных запросов при быстрой прокрутке.
  • hasReachedMax — если пришло меньше элементов, чем размер страницы, данные закончились.
  • Новые товары добавляются к старым через spread [...a, ...b], состояние не мутируется.
  • Подробнее о пагинации — в уроке Пагинация в архитектуре приложения.

Пример: тест для Bloc

Даже пара тестов сильно выделяет решение среди других:

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

class MockGetProducts extends Mock implements GetProductsUseCase {}

void main() {
  late MockGetProducts getProducts;
  setUp(() => getProducts = MockGetProducts());

  blocTest<ProductsBloc, ProductsState>(
    'выдаёт success с товарами после загрузки',
    setUp: () => when(() => getProducts(page: 1, limit: 20))
        .thenAnswer((_) async => [testProduct]),
    build: () => ProductsBloc(getProducts),
    act: (bloc) => bloc.add(ProductsFetched()),
    expect: () => [
      isA<ProductsState>()
          .having((s) => s.status, 'status', ProductsStatus.success)
          .having((s) => s.products.length, 'length', 1),
    ],
  );
}
  • mocktail создаёт фейковый UseCase, чтобы тест не ходил в сеть.
  • blocTest отправляет событие и сравнивает выданные состояния с ожидаемыми.
  • Предполагается, что page в начальном состоянии равен 1, а testProduct — заранее созданный объект Product.

Критерии хорошего решения

Критерий Что видит проверяющий
Работоспособность Проект запускается по README, обязательные пункты выполнены, нет падений
Архитектура Слои разделены, логика не в виджетах, зависимости через DI
Обработка ошибок Нет сети, ошибка сервера, пустой ответ — пользователь видит понятный экран
Качество кода Понятные имена, нет dynamic без причины, нет дублирования, flutter analyze чистый
UI Аккуратно, без переполнений (жёлто-чёрных полос), работает на разных размерах экрана
Git Осмысленные коммиты, нет build/ и ключей в репозитории
README Описание, стек, инструкция запуска, скриншоты
Плюсы Тесты, дополнительные функции, анимации — только после обязательной части

Повторение: что нужно уметь

Тестовое задание объединяет всё, что вы прошли за месяцы 4–6. Пройдитесь по списку и отметьте, в чём уверены:

Состояние и сеть (месяц 4) - Объяснить разницу FutureBuilder и StreamBuilder, ChangeNotifier и Bloc. - Написать Bloc с событиями и состояниями, использовать BlocBuilder и BlocListener. - Сделать модель с json_serializable и Equatable, GET/POST-запросы через Dio, обработать DioException. - Подключить Firebase через FlutterFire CLI, сделать авторизацию.

Архитектура (месяц 5) - Разложить фичу по слоям data / domain / presentation, написать UseCase. - Зарегистрировать зависимости в GetIt. - Хранить токен в flutter_secure_storage, сделать пагинацию. - Найти проблему отрисовки через DevTools.

Чат и публикация (месяц 6) - Сделать чат на Realtime Database со Stream и правилами безопасности (чат, часть 1). - Объяснить отличие WebSocket от HTTP и Firebase. - Настроить flavors и собрать release (сборки). - Подключить Crashlytics и логирование через Talker (аналитика). - Оформить README и Makefile, пройти самопроверку по чек-листу code review (мок-интервью).

Советы по подготовке

  • Сделайте пример из урока полностью сами, засекая время. Так вы поймёте свой реальный темп.
  • Подготовьте шаблон проекта: структура папок, DI, базовые классы ошибок, тема. Но разберитесь в каждой его строке.
  • Повторите слабые темы по чек-листу выше — пересмотрите уроки и практику к ним.
  • Тренируйтесь объяснять: после каждой функции расскажите вслух, почему сделали именно так.
  • Высыпайтесь и не откладывайте на последнюю ночь — спешка рождает больше всего багов.

Типичные ошибки

  • Начинать с «плюсов» — тёмная тема готова, а пагинации нет.
  • Не задавать вопросов и гадать, что имелось в виду.
  • Сдавать без README или с README по умолчанию от flutter create.
  • Один коммит со всем проектом и папка build/ в репозитории.
  • Не проверять release-сборку — в debug всё работало, а в release падает из-за пустого --dart-define.
  • Копировать код, который не понимаете — на обсуждении это выяснится.

Практика

  1. Выпишите требования примера задания в два списка — «обязательно» и «плюсы» — и составьте план на 5 дней. Ожидаемый результат: план с экранами, слоями и пакетами, как в шаге 2.
  2. Создайте каркас проекта: структура Clean Architecture, GetIt, Dio с интерцептором Talker, README. Ожидаемый результат: проект запускается и проходит flutter analyze.
  3. Реализуйте обязательную часть задания самостоятельно, коммитя после каждого шага. Ожидаемый результат: работающий каталог с пагинацией, состояниями ошибок и детальным экраном.
  4. Добавьте два «плюса» и минимум два теста для Bloc. Ожидаемый результат: flutter test проходит.
  5. Проведите самопроверку по таблице критериев и попросите одногруппника запустить проект по README. Ожидаемый результат: проект запускается без ваших подсказок, а замечания исправлены.

Итоги

  • Тестовое задание проверяет не только результат, но и то, как вы думаете и оформляете код.
  • Делайте его сами: на обсуждении спросят о каждой строке, а пробелы лучше найти сейчас.
  • Порядок работы: прочитать и уточнить → спланировать → сделать минимум → полировка → самопроверка.
  • Хорошее решение — работает, разделено по слоям, обрабатывает ошибки, имеет понятные коммиты и README.
  • Чек-лист «что нужно уметь» покрывает темы месяцев 4–6 — повторите то, в чём не уверены.
  • Условия отбора в Geeks Pro уточняйте у менторов.
Отзыв