Рекламный баннер

Тестовое задание frontend-разработчика: 5 примеров с разбором и чек-листом проверки

Тестовое задание frontend-разработчика обычно приходит после первого созвона. В письме написано «на пару вечеров», а что именно будут проверять, не сказано. Отсюда типичные перекосы: кто-то полирует пиксели, кто-то тащит в маленькое задание Redux и микрофронтенды.

Ниже разберём, что смотрят в тестовом на junior и middle, какие форматы встречаются чаще всего и сколько времени на них разумно тратить. Главная часть статьи: пять примеров заданий с условием, планом решения, подвохами и типичной ошибкой. В конце чек-лист перед отправкой и шаблон README.

Что на самом деле проверяют в тестовом задании frontend

Проверяющий редко оценивает «красоту» в отрыве от остального. Он открывает репозиторий, читает README, запускает проект и идёт по ТЗ построчно. Почти всегда смотрят четыре вещи:

  • Запускается ли проект по инструкции без ручных правок.
  • Насколько читаем код: структура папок, имена, размер компонентов.
  • Внимание к ТЗ и краевым случаям: пустой список, ошибка сети, длинный текст, медленный ответ.
  • Как вы объясняете решения: что сделали, что сознательно не сделали и почему.

Эти критерии повторяются у разных авторов. В разборе DOU 2018 года компании называли стиль кода, обработку ошибок, валидацию, краевые случаи и запуск приложения. В материале HTML Academy на Хабре про проверку вёрстки отдельно упоминали семантику, отсутствие inline-стилей и обработчиков в разметке, аккуратный README и структуру папок. Инструменты с тех пор сменились, а подход остался тем же.

В таблице ниже собрали, какие форматы обычно дают на разных уровнях и на что смотрят в каждом. Время указано как ориентир: точный срок задаёт компания, его стоит уточнять до старта.

УровеньТипичный форматОриентир по времениНа что смотрят
JuniorВёрстка по макету или небольшое приложение на чистом JavaScriptДо 3 часов (мнение участников Хабр Q&A)Семантика, адаптив, состояния формы, аккуратный репозиторий
Junior+ / middleReact-приложение с публичным APIСрок называет компания, объём уточняйте заранееЗагрузка, ошибки и пустые состояния, гонки запросов, структура компонентов
MiddleTypeScript, сложное состояние, тестыСрок называет компания, объём уточняйте заранееТипизация без any, архитектура состояния, тесты поведения, обоснование выбора

Отдельный вопрос, стоит ли вообще браться за бесплатное задание. Его мы подробно разобрали в статье делать ли тестовое бесплатно: разбор за и против. Здесь сосредоточимся на том, как сделать задание хорошо, если вы решили его делать.

Типичные форматы тестовых и сколько времени они занимают

Вёрстка по макету

Классика для junior: страница или блок по макету в Figma, несколько брейкпоинтов, состояния кнопок и форма. Проверяют умение читать макет и писать предсказуемый CSS. Pixel-perfect как самоцель сейчас ценят меньше, чем корректную адаптивность и доступность.

Мини-приложение на React с публичным API

Самый частый формат для junior+ и middle. Нужно получить данные, показать список, обработать загрузку и ошибку, иногда сделать страницу детали. Здесь хорошо видно, понимает ли кандидат жизненный цикл компонента и асинхронность. React выбирают чаще остального: по опросу Stack Overflow 2025 года им пользуются 44,7% всех респондентов.

Задачи на JavaScript и TypeScript

Иногда вместо приложения присылают набор функций: преобразовать данные, написать debounce, типизировать API-ответ. Такие задания ближе к лайвкодингу, только без наблюдателя. Если хотите потренироваться именно на них, посмотрите наш разбор алгоритмических задач на лайвкодинге.

Большое «боевое» задание

Если ТЗ похоже на кусок реального продукта со своим бэкендом, авторизацией и админкой, это повод остановиться. Уточните объём, срок и формат обратной связи до начала работы. Честная компания объяснит, зачем ей задание такого размера, или сократит его.

5 примеров тестовых заданий с разбором

Условия ниже собраны по типичным формулировкам работодателей и публичным ТЗ на GitHub. Мы не привязываем их к конкретным компаниям: важна логика решения, а не название в шапке.

Пример 1. Junior, вёрстка: адаптивный лендинг по макету

Условие. Сверстать страницу из 3–4 секций по макету в Figma. Три брейкпоинта, hover и focus у интерактивных элементов, форма заявки с валидацией. Без фреймворков, сборщик на ваш выбор.

Что сделать по шагам. Сначала разметка без стилей: header, main, section, form, footer. Затем сетка на Grid или Flexbox, CSS-переменные для цветов и отступов, типографика через clamp(). Картинкам задайте width и height, чтобы страница не прыгала при загрузке, и подготовьте srcset. Иконки лучше вставить как SVG.

Подвох. Форма. Проверяющий почти наверняка отправит пустые поля и неверный email. Используйте атрибуты required, type="email" и покажите понятное сообщение об ошибке рядом с полем.

:root {
  --gap: clamp(16px, 2vw, 32px);
}
.hero__title {
  font-size: clamp(28px, 4vw, 56px);
}
.button:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

Что увидит проверяющий. Откроет страницу на 320, 768 и 1440 пикселях, пройдётся по ней клавиатурой и заглянет в разметку. Семантика и видимый фокус часто весят больше, чем совпадение отступов до пикселя.

Типичная ошибка. Сплошные div вместо смысловых тегов, фиксированная ширина в пикселях и форма без состояния ошибки.

Пример 2. Junior, JavaScript без фреймворка: todo-список

Условие. Список задач: добавить, отметить выполненной, удалить. Фильтры «все», «активные», «выполненные». Данные сохраняются после перезагрузки страницы.

Что сделать по шагам. Храните состояние в одном объекте и рисуйте интерфейс из него. Каждое действие меняет состояние, сохраняет его и вызывает перерисовку. Клики ловите одним обработчиком на списке через делегирование событий.

const KEY = 'todos';
function load() {
  try {
    return JSON.parse(localStorage.getItem(KEY)) ?? [];
  } catch {
    return [];
  }
}
function renderItem(todo) {
  const li = document.createElement('li');
  li.textContent = todo.title; // не innerHTML
  li.dataset.id = todo.id;
  return li;
}

Подвох. Испорченные данные в localStorage и пользовательский ввод. JSON.parse без try/catch уронит приложение, а вставка текста через innerHTML открывает дорогу XSS.

Что увидит проверяющий. Добавит задачу с тегом script в названии, перезагрузит страницу, переключит фильтры. Потом откроет код и проверит, где хранится правда о данных.

Типичная ошибка. DOM как источник правды: состояние читают из классов и текста элементов. Такой код быстро ломается при добавлении фильтров.

Пример 3. Junior+ и middle, React и API: поиск с пагинацией

Условие. Поле поиска по публичному API, например GitHub Search API. Список результатов с загрузкой, ошибкой и пустым состоянием. Пагинация или бесконечная прокрутка, страница с деталями.

Что сделать по шагам. Разделите приложение на страницы, компоненты и хуки. Поисковый запрос держите в параметрах URL: тогда работает кнопка «назад» и ссылкой на результат можно поделиться. Запрос отправляйте с задержкой после ввода и отменяйте устаревший.

useEffect(() => {
  if (!query) return;
  const controller = new AbortController();
  setStatus('loading');
  fetch(`${API}?q=${encodeURIComponent(query)}&page=${page}`, {
    signal: controller.signal,
  })
    .then((r) => (r.ok ? r.json() : Promise.reject(r.status)))
    .then((data) => { setItems(data.items); setStatus('done'); })
    .catch((e) => { if (e.name !== 'AbortError') setStatus('error'); });
  return () => controller.abort();
}, [query, page]);

Подвох. Гонка запросов. Пользователь быстро печатает, ответы приходят в другом порядке, и на экране оказываются результаты старого запроса. AbortController и debounce закрывают проблему.

Что увидит проверяющий. Включит медленную сеть в DevTools, введёт запрос с опечаткой, выключит интернет. Каждое из трёх состояний должно выглядеть осмысленно, а не как пустой белый экран.

Типичная ошибка. Запрос на каждый символ, useEffect без очистки и индекс массива вместо стабильного key.

Пример 4. Middle, TypeScript: трекер времени

Условие. Похожее ТЗ лежит в открытом доступе на GitHub: трекер задач с запуском, остановкой, переименованием и удалением, React и TypeScript, any запрещён. Иногда добавляют сохранение на сервер и статистику за день.

Что сделать по шагам. Начните с доменных типов: задача, интервал, статус. Статусы опишите через объединение с дискриминатором, тогда TypeScript сам подскажет забытые ветки. Данные извне принимайте как unknown и проверяйте перед использованием.

type Task =
  | { status: 'idle'; id: string; title: string }
  | { status: 'running'; id: string; title: string; startedAt: number };

function isTask(x: unknown): x is Task {
  return typeof x === 'object' && x !== null
    && 'id' in x && 'status' in x;
}

Подвох. Запрет any легко обойти через as, и проверяющий это знает. Приведение типов без проверки выглядит так же плохо, как any.

Что увидит проверяющий. Включит strict в tsconfig, поищет as и ts-ignore, посмотрит, как типизирован ответ сервера и общий хук запроса.

Типичная ошибка. Типы только на пропсах компонентов, а внешние данные проходят без проверки.

Пример 5. Middle, архитектура и тесты: канбан-доска

Условие. Колонки и карточки без ограничения по количеству, перенос карточек между колонками, адаптивная вёрстка. Такой формат часто описывают соискатели в обсуждениях тестовых на React.

Что сделать по шагам. Храните данные нормализованно: колонки отдельно, карточки отдельно, порядок списком id. Изменения проводите через reducer, чтобы логику можно было протестировать без интерфейса. Добавьте перенос с клавиатуры: это вопрос доступности, а не украшение.

test('переносит карточку в другую колонку', async () => {
  render(<Board initial={data} />);
  await user.click(screen.getByRole('button', { name: /в работу/i }));
  const column = screen.getByRole('region', { name: 'В работе' });
  expect(within(column).getByText('Сверстать шапку')).toBeInTheDocument();
});

Подвох. Тесты. Хватит трёх-пяти тестов на поведение: добавление, перенос, пустая колонка. Тесты на внутреннее состояние компонента ломаются при любом рефакторинге и говорят проверяющему меньше.

Что увидит проверяющий. Деплой на Vercel или Netlify, сохранение после перезагрузки, работу с клавиатуры и запуск тестов одной командой.

Типичная ошибка. Вложенные массивы карточек внутри колонок, из-за которых перенос превращается в цепочку копирований.

Как оформить репозиторий и README, чтобы проверяющему было легко

README читают первым, и по нему решают, сколько сил тратить на проверку. Хороший README отвечает на пять вопросов: как запустить, что сделано, что не сделано, какие допущения вы приняли и почему выбрали этот стек.

  • Запуск за три команды и версия Node, на которой всё проверено.
  • Список выполненных пунктов ТЗ и честный список того, что не успели.
  • Допущения по неясным местам ТЗ.
  • Ссылка на деплой или короткая запись экрана.
  • Аккуратная история коммитов, .gitignore, никаких node_modules и ключей API.
# Поиск репозиториев GitHub

Демо: ссылка на деплой

## Запуск
Node 20+
npm install
npm run dev
npm test

## Что сделано
- Поиск с задержкой 400 мс и отменой запросов
- Пагинация, состояния загрузки, ошибки и пустого результата
- Страница репозитория

## Что не сделано и почему
- Кеширование запросов: не требовалось в ТЗ

## Допущения
- Поиск начинается с 3 символов

Перед отправкой склонируйте репозиторий в чистую папку и пройдите инструкцию с нуля. Если хотите, чтобы задание работало и как витрина, посмотрите, как оформить GitHub и pet-проекты.

Критерии код-ревью: чек-лист перед отправкой

Пройдитесь по списку за час до дедлайна. Каждый пункт проверяется за пару минут, а вместе они закрывают большую часть замечаний на ревью.

  • Каждый пункт ТЗ выполнен или явно отмечен в README как невыполненный.
  • Есть состояния загрузки, ошибки и пустого результата.
  • У картинок есть alt, у полей есть label, фокус виден.
  • Вёрстка не ломается на 320, 768 и 1440 пикселях.
  • Нет any и необоснованных приведений типов.
  • Имена переменных и компонентов говорят, что они делают.
  • Нет мёртвого кода, закомментированных блоков и console.log.
  • Каждая зависимость нужна, и её выбор можно объяснить.
  • Линтер и форматтер настроены и проходят без ошибок.
  • Ключевая логика покрыта хотя бы несколькими тестами.
  • Проект запускается по README в чистой папке.
  • Ссылка на деплой открывается без авторизации.

Если после ревью пришёл список замечаний, это хороший знак: с вами готовы разговаривать. Как превратить критику в плюс для себя, мы писали в статье как реагировать на код-ревью.

8 типичных ошибок и как их не допустить

1. Не дочитали ТЗ. Пропущенный пункт про сортировку или тесты заметят сразу. Перед стартом перепишите ТЗ в чек-лист и отмечайте выполненное.

2. Переусложнили. Redux, микрофронтенды и собственный UI-кит в todo-списке говорят не о силе, а о плохом чувстве меры. Берите самое простое решение, которое закрывает ТЗ.

3. Скопировали без понимания. Кусок из чужого репозитория выдаёт себя другим стилем кода. На созвоне по заданию вопрос «почему так» прозвучит именно про него.

4. Проигнорировали ошибки сети. Приложение работает только при идеальном интернете. Отключите сеть в DevTools и посмотрите, что увидит пользователь.

5. Не написали README. Проверяющему приходится угадывать команды запуска. Пять минут на README экономят ему полчаса.

6. Сдали после дедлайна молча. Если не успеваете, напишите заранее и предложите новый срок. Молчание считывают как необязательность.

7. Сделали красиво, но без логики. Анимации и тени не спасут, если фильтр работает через раз. Сначала функциональность, потом полировка.

8. Не задали вопросов. Неясный пункт ТЗ лучше уточнить письмом, чем угадать. Если ответа нет, зафиксируйте допущение в README.

Сколько времени тратить и когда отказаться

Общепринятой нормы нет. В обсуждении на Хабр Q&A опытные разработчики советовали junior браться за задания примерно до 3 часов, а за объёмные просить оплату. В разборе DOU 2018 года компании называли ориентиры от 2 до 4 часов. Это мнения и старые примеры, а не стандарт рынка, поэтому срок лучше уточнить у конкретной компании.

Красные флаги: задание похоже на боевую задачу продукта, нет обещания обратной связи, после сдачи просят «немного доработать». Если сомневаетесь, отправьте короткое письмо до начала работы:

Спасибо за задание. Уточните, пожалуйста, сколько времени вы закладываете на него и какие пункты обязательны. Планирую сдать до пятницы и описать в README, что не успею. Будет ли обратная связь по результату?

Соотносите объём задания с вакансией. По данным страницы зарплат frontend-разработчиков на HireHi на 14.10.2026, медиана по 795 вакансиям с зарплатой за 12 месяцев составляет 180 000 ₽ на руки. Для junior медиана 60 000 ₽, для middle 150 000 ₽. Неделя бесплатной работы ради вакансии с вилкой junior выглядит несоразмерно.

Можно ли использовать AI-ассистентов в тестовом

Если ТЗ прямо не запрещает, ассистент можно использовать как помощника: для шаблонного кода, тестов, подсказок по API. Важное условие: вы должны понимать и уметь объяснить каждую строку. После тестового часто бывает созвон, где просят пройтись по решению и что-то поменять на ходу.

Честнее всего написать об этом в README: какие части сделаны с ассистентом и что вы проверили вручную. Подробнее о том, как работать с такими инструментами и не терять навык, читайте в статье про AI-ассистентов в разработке.

Как подготовиться заранее

Лучшая подготовка к тестовому: решить несколько похожих заданий до того, как придёт настоящее. Для junior подойдёт план на две недели.

  • Дни 1–3: вёрстка лендинга по бесплатному макету, проверка на трёх ширинах и с клавиатуры.
  • Дни 4–6: todo-список на чистом JavaScript с localStorage и фильтрами.
  • Дни 7–11: React-приложение с публичным API, состояниями и пагинацией.
  • Дни 12–14: шаблон репозитория с линтером, тестами и README, который потом копируете под каждое задание.

Практику удобно брать из открытого репозитория Hexlet ru-test-assignments на GitHub. Там собрано около 400 тестовых заданий примерно от 200 компаний, есть раздел frontend. Решений там нет, и это нормально: решайте на время и сверяйте результат со своим чек-листом. Если вы только входите в профессию, начните с гайда о том, как стать frontend-разработчиком.

Свежие вакансии удобно смотреть в разделе frontend-вакансий на HireHi. В описаниях часто прямо написано, будет ли тестовое и какой у компании стек, а по грейду и формату можно сразу отфильтровать подходящие.

Лента вакансий frontend-разработчиков на HireHi в виде списка
Вакансии frontend-разработчиков на HireHi.

После тестового часто бывает разговор о решении. Его можно отрепетировать в ИИ-тренажёре: выберите «Разработка», «Frontend», свой грейд и техническое собеседование. Тренажёр задаёт вопросы голосом и разбирает ответы.

Настройка технического мок-собеседования для frontend-разработчика уровня middle на HireHi
Настройка технического собеседования для frontend в ИИ-тренажёре.

Отрепетируйте защиту решения

Тренажёр задаст вопросы по frontend вашего уровня и покажет, где ответы звучат неуверенно.

Начать тренировку

часто задаваемые вопросы

Единой нормы нет. В обсуждениях на Хабр Q&A опытные разработчики называли разумным пределом для junior около 3 часов. На практике вёрстка лендинга или todo-список укладываются в один-два вечера, если не уходить в полировку. Если задание явно больше, уточните у компании ожидаемый объём и обязательные пункты до начала работы.

Да, тестовые дают и мидлам, просто другого содержания: TypeScript, сложное состояние, тесты, архитектурные решения. Если у вас сильное портфолио, можно предложить альтернативу: разбор вашего открытого проекта или техническое интервью вместо задания. Часть компаний на это соглашается, если предложить заранее и вежливо.

Можно, если задача этого требует. Для одностраничной вёрстки фреймворк лишний, а для приложения с API и несколькими экранами React уместен. Объясните выбор в README одной-двумя фразами. Если в вакансии указан другой фреймворк, например Vue, разумнее делать на нём: так задание покажет то, что нужно компании.

Напишите до дедлайна, а не после. Коротко объясните причину и предложите новую дату или вариант сдать часть с честным описанием в README. Перенос на день-два с предупреждением выглядит рабочей ситуацией. Молчаливая сдача с опозданием снижает шансы сильнее, чем неполное решение.

Если они указаны в ТЗ, да. Если нет, для junior это плюс, но не условие. Для middle TypeScript и несколько тестов на ключевую логику почти всегда ожидают по умолчанию. TypeScript распространён широко: по опросу Stack Overflow 2025 года им пользуются 43,6% всех респондентов. Лучше три осмысленных теста на поведение, чем десяток формальных.

Через неделю после сдачи можно вежливо напомнить о себе одним письмом. Если ответа нет, двигайтесь дальше и используйте задание как pet-проект в портфолио, если условия это не запрещают. Почему компании пропадают после этапов отбора, мы разбирали в статье о причинах молчания работодателей .

Итог

  • В тестовом проверяют не красоту, а запуск по README, читаемый код, внимание к ТЗ и умение объяснить решения.
  • Для junior типичны вёрстка и маленькое приложение на JavaScript, для middle React с API, TypeScript и тесты.
  • Состояния загрузки, ошибки и пустого результата отличают аккуратное решение от учебного.
  • Срок и объём уточняйте до старта, а неясные места фиксируйте в README как допущения.

Потренируйтесь на двух-трёх заданиях заранее, соберите шаблон репозитория и отрепетируйте защиту решения. Подходящие вакансии ищите в разделе frontend-вакансий, а перед откликом прогоните резюме через проверку резюме, чтобы тестовое вам вообще прислали.