Modern QA2026Подготовка к техническим оценкам
Join

Course25 Interview Preparation & Career

Foundations · Chapter 25

Подготовка к техническим оценкам

Updated Jul 2026

Что на самом деле проверяют технические собеседования для QA

Технические собеседования для QA-инженеров принципиально отличаются от собеседований для разработчиков. Вас редко просят реализовать алгоритм сортировки или создать REST API с нуля. Вместо этого вас оценивают по способности систематически мыслить о тестировании, писать чистый и поддерживаемый код автоматизации тестов и проектировать тестовые стратегии для сложных систем.

Четыре наиболее распространённых формата: live coding, домашние задания, упражнения «как бы вы протестировали X» и проектирование систем для тестирования. Каждый требует разной подготовки. Этот файл охватывает все четыре.

Формат 1: Live Coding

Чего ожидать

Вам дают задачу по кодированию, связанную с тестированием, и 30-60 минут на её решение, пока интервьюер наблюдает. Задача обычно включает одно из:

  • Написание Page Object Model с нуля
  • Создание автоматизации тестирования API с assertions
  • Отладка падающего теста
  • Парсинг и валидация тестовых данных
  • Написание утилитарной функции для тестовой инфраструктуры

Пример задачи: создать Page Object Model

Условие: «Вот страница логина с полем имени пользователя, полем пароля, чекбоксом "Запомнить меня" и кнопкой отправки. Напишите Page Object и тест, который верифицирует успешный логин, неудачный логин с неправильным паролем и функциональность "Запомнить меня".»

Как выглядит сильный ответ (Playwright + TypeScript):

// login.page.ts
import { Page, Locator } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly usernameInput: Locator;
  readonly passwordInput: Locator;
  readonly rememberMeCheckbox: Locator;
  readonly submitButton: Locator;
  readonly errorMessage: Locator;

  constructor(page: Page) {
    this.page = page;
    this.usernameInput = page.getByLabel('Username');
    this.passwordInput = page.getByLabel('Password');
    this.rememberMeCheckbox = page.getByLabel('Remember me');
    this.submitButton = page.getByRole('button', { name: 'Sign in' });
    this.errorMessage = page.getByRole('alert');
  }

  async goto() {
    await this.page.goto('/login');
  }

  async login(username: string, password: string, rememberMe = false) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    if (rememberMe) {
      await this.rememberMeCheckbox.check();
    }
    await this.submitButton.click();
  }
}
// login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from './login.page';

test.describe('Login', () => {
  let loginPage: LoginPage;

  test.beforeEach(async ({ page }) => {
    loginPage = new LoginPage(page);
    await loginPage.goto();
  });

  test('successful login redirects to dashboard', async ({ page }) => {
    await loginPage.login('validuser', 'validpass');
    await expect(page).toHaveURL('/dashboard');
  });

  test('wrong password shows error message', async () => {
    await loginPage.login('validuser', 'wrongpass');
    await expect(loginPage.errorMessage).toBeVisible();
    await expect(loginPage.errorMessage).toHaveText(
      'Invalid username or password'
    );
  });

  test('remember me persists session after browser restart', async ({
    page,
    context,
  }) => {
    await loginPage.login('validuser', 'validpass', true);
    await expect(page).toHaveURL('/dashboard');

    // Verify cookie is set with extended expiry
    const cookies = await context.cookies();
    const sessionCookie = cookies.find((c) => c.name === 'session');
    expect(sessionCookie).toBeDefined();
    expect(sessionCookie!.expires).toBeGreaterThan(Date.now() / 1000 + 86400);
  });
});

Что оценивает интервьюер:

  • Стратегия локаторов, ориентированная на пользователя (getByLabel, getByRole), а не хрупкие CSS-селекторы
  • Разделение взаимодействий со страницей и утверждений тестов
  • Осмысленные имена тестов, описывающие ожидаемое поведение
  • Правильное использование setup (beforeEach) и параметризации
  • Мышление граничными случаями (тест remember-me проверяет cookie, а не просто чекбокс)

Пример задачи: автоматизация тестирования API

Условие: «Напишите тесты для REST API эндпоинта POST /api/users, который создаёт нового пользователя. Эндпоинт принимает {name, email, role} и возвращает созданного пользователя с id. Протестируйте happy path и минимум 3 случая ошибок.»

import requests
import pytest

BASE_URL = "https://api.example.com"

class TestCreateUser:
    def test_create_user_success(self):
        payload = {"name": "Jane Doe", "email": "jane@example.com", "role": "tester"}
        response = requests.post(f"{BASE_URL}/api/users", json=payload)

        assert response.status_code == 201
        data = response.json()
        assert data["name"] == "Jane Doe"
        assert data["email"] == "jane@example.com"
        assert data["role"] == "tester"
        assert "id" in data
        assert isinstance(data["id"], int)

    def test_create_user_missing_required_field(self):
        payload = {"name": "Jane Doe"}  # missing email and role
        response = requests.post(f"{BASE_URL}/api/users", json=payload)

        assert response.status_code == 400
        assert "email" in response.json()["errors"]

    def test_create_user_invalid_email(self):
        payload = {"name": "Jane Doe", "email": "not-an-email", "role": "tester"}
        response = requests.post(f"{BASE_URL}/api/users", json=payload)

        assert response.status_code == 400
        assert "email" in response.json()["errors"]

    def test_create_user_duplicate_email(self, create_test_user):
        payload = {"name": "Another Jane", "email": create_test_user["email"], "role": "tester"}
        response = requests.post(f"{BASE_URL}/api/users", json=payload)

        assert response.status_code == 409

    def test_create_user_invalid_role(self):
        payload = {"name": "Jane Doe", "email": "jane2@example.com", "role": "superadmin"}
        response = requests.post(f"{BASE_URL}/api/users", json=payload)

        assert response.status_code == 400

Пример задачи: отладка падающего теста

Условие: «Этот тест проходит локально, но падает в CI. Найдите баг.»

def test_report_generation():
    report = generate_report(start_date="2025-01-01", end_date="2025-01-31")
    assert report.title == "Monthly Report - January 2025"
    assert report.generated_at.date() == datetime.date.today()

Что нужно выявить: Тест хрупкий, потому что datetime.date.today() возвращает разные значения в зависимости от того, когда и где он выполняется. В CI часовой пояс может отличаться от локального, и если тест выполняется около полуночи, дата может отличаться на один день. Исправление -- замокать datetime.date.today() или проверять попадание во временное окно, а не точную дату.

Формат 2: упражнения «Как бы вы протестировали X?»

Это самый распространённый формат QA-собеседования. Интервьюер называет систему, функцию или физический объект, и от вас ожидается систематическое выявление тестовых сценариев. Цель -- не перечислить все тест-кейсы, а продемонстрировать структурное мышление.

Структурированный подход

Используйте этот 5-шаговый фреймворк для любого вопроса «как бы вы протестировали»:

  1. Уточните требования: задайте 3-5 вопросов, прежде чем начнёте перечислять тесты
  2. Определите пользовательские персоны: кто использует это и как?
  3. Функциональное тестирование: happy path, граничные случаи, обработка ошибок
  4. Нефункциональное тестирование: производительность, безопасность, доступность, удобство использования
  5. Интеграционное и системное тестирование: как это взаимодействует с другими системами?

«Как бы вы протестировали страницу логина?»

Шаг 1 -- Уточнение: «Какие методы аутентификации поддерживаются? Есть ли MFA? Ограничение частоты запросов? Требования к сложности пароля? SSO?»

Шаг 2 -- Персоны: Новый пользователь, вернувшийся пользователь, администратор, заблокированный пользователь, пользователь на мобильном

Шаг 3 -- Функциональное:

Категория Тест-кейсы
Happy path Валидное имя пользователя + пароль, редирект на дашборд
Невалидные входные данные Неправильный пароль, несуществующее имя пользователя, пустые поля, попытки SQL-инъекции, XSS в имени пользователя
Граничные значения Максимальная длина имени пользователя, максимальная длина пароля, минимальная длина пароля
Обработка ошибок Ясное сообщение об ошибке (не раскрывает, какое поле неверно -- из соображений безопасности), поведение при повторной попытке
Управление состоянием Создание сессии, обработка cookie, конкурентные сессии, истечение сессии
Запомнить меня Персистентность cookie, последствия для безопасности, кросс-браузерное поведение
Забыли пароль Доставка email, истечение токена, предотвращение повторного использования ссылки

Шаг 4 -- Нефункциональное:

  • Производительность: Логин под нагрузкой (1000 одновременных пользователей), время ответа < 2 с
  • Безопасность: Защита от brute force, блокировка аккаунта после N попыток, принудительный HTTPS, пароль не логируется в plaintext, защита от CSRF
  • Доступность: Совместимость с screen reader, навигация клавиатурой, контрастность цветов в сообщениях об ошибках
  • Удобство использования: Порядок табуляции, поведение автозаполнения, ясность сообщений об ошибках

Шаг 5 -- Интеграция: Интеграция с провайдером OAuth/SSO, верификация аудит-лога, система уведомлений (логин с нового устройства)

«Как бы вы протестировали лифт?»

Этот классический вопрос проверяет, можете ли вы применять систематическое мышление к непрограммной системе.

Уточнение: Сколько этажей? Сколько лифтов? Есть ли ограничение по весу? Есть ли требования к доступности?

Категория Тест-кейсы
Базовая функциональность Нажать кнопку, лифт прибывает. Выбрать этаж, лифт едет туда. Двери открываются и закрываются.
Граничные случаи Нажать все кнопки этажей одновременно. Нажать этаж, на котором вы находитесь. Нажать «открыть двери» во время закрытия.
Нагрузочное тестирование Максимальная грузоподъёмность. Один человек ниже минимума. Ровно на пределе веса.
Безопасность Кнопка аварийной остановки. Датчик двери (объект в проёме). Поведение при отключении питания. Пожарный режим.
Доступность Кнопки с шрифтом Брайля. Аудио-объявления. Время открытия дверей для пользователей на инвалидных креслах. Низко расположенные кнопки.
Конкурентность Несколько людей нажимают кнопки на разных этажах. Два лифта оптимизируют эффективность.
Окружающая среда Работа во время землетрясения. Экстремальные температуры. Вода/затопление.
Удобство использования Обратная связь от кнопок (загорается при нажатии). Отображение индикатора этажа. Ожидания по времени ожидания.

«Как бы вы протестировали корзину покупок?»

Уточнение: Веб или мобильное? Какие методы оплаты? Есть ли гостевой чекаут? Какой максимальный размер корзины?

Категория Тест-кейсы
Добавление в корзину Один товар, несколько товаров, один и тот же товар несколько раз, максимальное количество
Удаление из корзины Удалить один товар, удалить все товары, удалить во время чекаута
Обновление количества Увеличить, уменьшить, установить в ноль, превысить запас
Ценообразование Цена за единицу, оптовые скидки, промокоды, расчёт налога по регионам, отображение валюты
Персистентность Корзина сохраняется после обновления страницы, сохраняется после выхода/входа, объединяется при входе (гостевая + аккаунт)
Запасы Обработка «нет в наличии», товар становится недоступным во время просмотра, race condition последнего товара
Производительность Корзина со 100 товарами, скорость пересчёта цены, конкурентные обновления корзины
Безопасность Манипуляция ценой через API, brute force промокодов, перехват сессии
Интеграция Платёжный шлюз, система складского учёта, калькулятор доставки, налоговый сервис
Доступность Screen reader озвучивает обновления корзины, навигация клавиатурой по товарам корзины

Формат 3: проектирование систем для тестирования

На собеседованиях уровня senior и architect вас могут попросить спроектировать фреймворк автоматизации тестирования с нуля.

«Спроектируйте фреймворк автоматизации тестирования для e-commerce платформы.»

Структурируйте ваш ответ по уровням:

Layer 5: Reporting & Analytics
    Test reports, dashboards, trend analysis, Slack/email notifications

Layer 4: CI/CD Integration
    GitHub Actions pipeline, parallel execution, quality gates, artifact storage

Layer 3: Test Suites
    Smoke (5 min), Regression (30 min), Full (2 hr), Performance (1 hr)

Layer 2: Test Infrastructure
    Page Objects, API clients, test data factory, fixtures, utilities

Layer 1: Frameworks & Tools
    Playwright (browser), pytest/requests (API), k6 (performance), axe-core (accessibility)

Ключевые проектные решения для обсуждения:

  • Почему Playwright, а не Selenium: Auto-waiting, встроенное тестирование API, лучшая поддержка TypeScript, мультибраузерность без дополнительных драйверов (глава 13)
  • Стратегия тестовых данных: Паттерн factory для создания тестовых данных, setup через API вместо UI, очистка в afterEach хуках
  • Параллелизация: Шардирование по файлам тестов, независимые тестовые данные для каждого шарда, архитектура без общего состояния
  • Управление средами: Конфигурация по средам, управление секретами, feature flags для управления тестами
  • Отчётность: HTML-отчёты, сохранённые как артефакты CI, уведомления в Slack при сбое, дашборд трендов в Grafana

Формат 4: домашние задания

Что включить

Компонент Почему это важно
Ясный README с инструкциями по настройке Показывает, что вы думаете о читателе, а не только о коде
Чистая структура проекта Показывает, что вы умеете организовать реальный фреймворк, а не просто писать скрипты
Несколько типов тестов UI, API и хотя бы один нефункциональный тест показывают широту
Конфигурация CI Работающий файл GitHub Actions показывает, что вы думаете о полном жизненном цикле
Осмысленные assertions Проверяйте поведение, а не детали реализации
Обработка ошибок Тесты, которые выдают ясные сообщения о сбоях, а не stack traces
Комментарии к коду (умеренно) Объясняйте «почему», а не «что». Комментарии к архитектурным решениям, а не к очевидному коду.

Частые ошибки в домашних заданиях

Ошибка Почему это вредит Исправление
Нет README Ревьюер не может запустить ваши тесты Напишите README с разделами setup, run и architecture
Тесты зависят друг от друга Один сбой каскадируется, маскируя реальные проблемы Каждый тест должен быть независимым и идемпотентным
Захардкоженные тестовые данные Тесты ломаются в разных средах Используйте переменные окружения или конфигурационные файлы
Нет негативных тестов Показывает только happy-path мышление Включите минимум 30% негативных тестов и тестов граничных случаев
Переусложнение Домашнее задание -- ограниченное по времени упражнение, а не продакшен-фреймворк Постройте то, что просят, добавьте раздел «будущие улучшения» в README
Нет CI Показывает, что вы не думаете об автоматизации целиком Добавьте простой GitHub Actions workflow, даже если базовый

Советы по live coding

Прежде чем начать кодировать

  1. Переформулируйте задачу: «Итак, мне нужно написать тесты для REST API, который управляет списком задач. Я покрою CRUD-операции, валидацию и обработку ошибок. Это соответствует вашим ожиданиям?»
  2. Задайте уточняющие вопросы: Аутентификация? Ограничение частоты запросов? Пагинация? Интервьюер хочет видеть, что вы думаете перед тем, как кодировать.
  3. Обозначьте свой подход: «Я начну с Page Object, затем напишу happy path тест, затем добавлю граничные случаи. Я буду настраивать тестовые данные напрямую через API.»

Во время кодирования

  • Думайте вслух: «Я использую getByRole здесь вместо CSS-селектора, потому что это более устойчиво к изменениям разметки и соответствует тому, как пользователи взаимодействуют со страницей.»
  • Начните с простейшего теста: Получите что-то проходящее, затем добавляйте сложность.
  • Обрабатывайте ошибки спокойно: Если вы столкнулись с синтаксической ошибкой, не паникуйте. Дебажьте вслух: «Думаю, проблема с async/await здесь -- дайте мне проверить тип возвращаемого значения.»
  • Давайте хорошие имена: test('should show error when password is too short') лучше, чем test('test3').

Управление временем

Временной блок Активность
Первые 5 минут Уточнить требования, обозначить подход, настроить структуру
Минуты 5-35 Написать основное решение: Page Objects или API-клиент, 3-4 теста
Минуты 35-50 Добавить граничные случаи, почистить код, добавить assertions
Последние 10 минут Запустить тесты (если применимо), объяснить проектные решения, обсудить, что бы вы добавили при большем времени

Если вы застряли

  • Скажите вслух: «Я застрял на том, как обработать токен аутентификации. Дайте мне подумать...»
  • Упростите: «Я знаю, что идеальный подход -- использовать fixture, но для экономии времени я пропишу setup inline и отмечу, что я бы отрефакторил.»
  • Попросите подсказку: Это не признак слабости. «Можете напомнить API-эндпоинт для создания тестовых данных?» -- это абсолютно нормально.

Перекрёстные ссылки на главы для технических собеседований

Техническое собеседование опирается почти на каждую главу этого руководства. Вот наиболее часто проверяемые области:

Тема собеседования Релевантные главы
Автоматизация браузера и Page Objects Глава 1 (Agent Skills), глава 13 (Selenium/WebDriver)
Тестирование API и assertions Глава 4 (API Contract Testing), глава 14 (Основы API)
Проектирование CI/CD-пайплайнов Глава 16 (CI/CD Pipelines)
Тестовая стратегия и оценка рисков Глава 22 (Тестовая стратегия и метрики качества)
Подход к тестированию производительности Глава 5 (Performance и Chaos Engineering)
Вопросы тестирования безопасности Глава 7 (Security Testing для AI-приложений)
Тестирование доступности Глава 10 (Визуальное тестирование и доступность)
Базы данных и тестовые данные Глава 15 (SQL и тестирование баз данных)
Основы программирования Глава 12 (Программирование для QA)

Практическое упражнение

  1. Создайте небольшой проект автоматизации тестирования (Page Object + 5 тестов) и засеките время -- стремитесь к 45 минутам. Это имитирует сессию live coding.
  2. Отработайте фреймворк «как бы вы протестировали» на 3 разных системах: одна программная функция, один физический объект и один API-эндпоинт.
  3. Спроектируйте фреймворк автоматизации тестирования на доске (или пустом документе) для продукта, которым вы пользуетесь ежедневно. Объясните каждый уровень за 1 минуту.
  4. Выполните домашнее задание для open source проекта: напишите тесты для публичного API (например, JSONPlaceholder, PokéAPI) с полным README и конфигурацией CI.
  5. Запишите себя во время упражнения live coding. Просмотрите запись и определите моменты, когда вы замолчали, застряли или могли бы лучше объяснить своё мышление.