Подготовка к техническим оценкам
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-шаговый фреймворк для любого вопроса «как бы вы протестировали»:
- Уточните требования: задайте 3-5 вопросов, прежде чем начнёте перечислять тесты
- Определите пользовательские персоны: кто использует это и как?
- Функциональное тестирование: happy path, граничные случаи, обработка ошибок
- Нефункциональное тестирование: производительность, безопасность, доступность, удобство использования
- Интеграционное и системное тестирование: как это взаимодействует с другими системами?
«Как бы вы протестировали страницу логина?»
Шаг 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
Прежде чем начать кодировать
- Переформулируйте задачу: «Итак, мне нужно написать тесты для REST API, который управляет списком задач. Я покрою CRUD-операции, валидацию и обработку ошибок. Это соответствует вашим ожиданиям?»
- Задайте уточняющие вопросы: Аутентификация? Ограничение частоты запросов? Пагинация? Интервьюер хочет видеть, что вы думаете перед тем, как кодировать.
- Обозначьте свой подход: «Я начну с 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) |
Практическое упражнение
- Создайте небольшой проект автоматизации тестирования (Page Object + 5 тестов) и засеките время -- стремитесь к 45 минутам. Это имитирует сессию live coding.
- Отработайте фреймворк «как бы вы протестировали» на 3 разных системах: одна программная функция, один физический объект и один API-эндпоинт.
- Спроектируйте фреймворк автоматизации тестирования на доске (или пустом документе) для продукта, которым вы пользуетесь ежедневно. Объясните каждый уровень за 1 минуту.
- Выполните домашнее задание для open source проекта: напишите тесты для публичного API (например, JSONPlaceholder, PokéAPI) с полным README и конфигурацией CI.
- Запишите себя во время упражнения live coding. Просмотрите запись и определите моменты, когда вы замолчали, застряли или могли бы лучше объяснить своё мышление.