Перейти к основному содержимому

РЕАЛЬНОЕ СОБЕСЕДОВАНИЕ QA Engineer Ozon - Middle

· 80 мин. чтения

Сегодня мы разберем живое техническое интервью на позицию мобильного тестировщика, в котором кандидат с честностью демонстрирует свой реальный опыт: уверенное владение базовыми инструментами (эмуляторы, сниферы, диплинки, пуши), но пробелы в работе с логами, аналитикой и формализацией тест-кейсов. Беседа показывает, как детальные вопросы интервьюера вскрывают зрелость процессов на прошлых проектах кандидата и умение (или неумение) мыслить как пользователь и как инженер, что делает это интервью показательным примером для разбора типичных ошибок джунов и мидлов.

Вопрос 1. В каких случаях выполняют ночные запуски автотестов и как оперативно реагируют на их результаты?

Таймкод: 00:07:18

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

Правильный ответ:
Ночные запуски автотестов обычно являются частью полноценного CI/CD процесса и используются для выполнения более тяжелых, долгих и комплексных проверок, которые нецелесообразно запускать при каждом коммите или во время активного рабочего дня.

Ключевые сценарии, когда запускают ночные прогоны:

  1. Комплексные регрессионные наборы:

    • Большой пул UI/интеграционных/сквозных (end-to-end) тестов, которые занимают десятки минут или часы.
    • Проверка на регрессии по всему продукту после серии изменений за день.
    • Часто включают тесты, которые:
      • нестабильны (flaky) и не крутятся на каждом коммите;
      • требуют большого количества внешних сервисов или сложной среды.
  2. Ресурсоемкие проверки:

    • Нагрузочные/стресс-тесты (performance, нагрузка на БД, профилирование).
    • Тесты, требующие выделенной инфраструктуры (отдельные стенды, фермы, дорогие окружения).
    • Ночью проще использовать максимум ресурсов (CI-агенты, тестовая ферма), не мешая day-to-day разработке.
  3. Кросс-среды и кросс-конфигурации:

    • Прогоны на разных версиях сервисов, баз данных, конфигураций, фич-флагов.
    • Проверка совместимости микросервисов, API контрактов, схем БД, миграций.
  4. Метрики качества и технический контроль:

    • Запуск расширенных статических анализаторов, линтеров, security-сканеров, проверок лицензий.
    • Генерация отчетов по покрытию кода, техническому долгу, деградации производительности.

Реакция на результаты по времени:

  • В большинстве зрелых процессов:
    • Ночные прогоны не предполагают немедленного пробуждения команды.
    • Результаты анализируются:
      • утром: ответственными за качество (owner-ы сервисов, QA/разработчики);
      • в дневное время: фиксация проблем, создание задач в трекере, определение приоритетов.
  • Критические падения:
    • Если ночные тесты выявили критический дефект в ветке, близкой к релизной:
      • блокируется релиз до выяснения причин;
      • может быть настроен авто-блок мёрджей или авто-rollback.
    • Если инфраструктура или тесты сломались (а не продукт):
      • задача уходит на починку пайплайна/окружения, но не воспринимается как продуктовый баг.

Пример интеграции с CI (Go):

package main

import (
"log"
"os"
"time"
)

func main() {
// Условный пример nightly-режима: запускаем расширенный набор тестов
mode := os.Getenv("TEST_MODE")
if mode == "nightly" {
log.Println("Starting nightly regression suite...")
runFullRegression()
sendReport()
} else {
log.Println("Starting regular pipeline tests...")
runSmokeTests()
}
}

func runFullRegression() {
// Имитация долгих прогонов
time.Sleep(10 * time.Second)
log.Println("Full regression completed")
}

func runSmokeTests() {
time.Sleep(2 * time.Second)
log.Println("Smoke tests completed")
}

func sendReport() {
// Обычно: отправка в Allure/ReportPortal/Slack/Email
log.Println("Nightly report generated and sent")
}

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

Вопрос 2. Кратко опиши свой последний проект, задачи и зону ответственности в тестировании.

Таймкод: 00:08:53

Ответ собеседника: правильный. Последний проект — мобильное банковское приложение с инвестиционным функционалом; занимался тестированием Android-версии на эмуляторе и реальном устройстве, iOS не покрывал из-за отсутствия техники и условий.

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

Пример сильного ответа:

  • Проект:

    • Мобильное банковское приложение с инвестиционным модулем: брокерский счет, торговля ценными бумагами, фонды, облигации, котировки в реальном времени, уведомления, интеграция с бэкендом банкинга, KYC/AML, безопасность.
    • Микросервисная архитектура на бэкенде: API для авторизации, профиля, сделок, портфеля, отчетности, интеграции с биржами и внешними провайдерами.
  • Зона ответственности:

    • Функциональное тестирование ключевых инвестиционных сценариев:
      • открытие/пополнение брокерского счета;
      • покупка/продажа акций, облигаций, фондов;
      • обработка заявок, статусы сделок, отмены;
      • корректное отображение портфеля, прибыли/убытков, комиссий, налогов.
    • Интеграционное тестирование взаимодействия мобильного клиента с backend API:
      • валидация контрактов (REST/JSON), кодов ответов, обработка ошибок;
      • кросс-проверка данных между мобильным приложением, логами и базой данных (например, проверка фиксации сделок, остатков, движений по счету).
    • Нефункциональные аспекты:
      • устойчивость при нестабильном соединении (сетевые деградации, таймауты, повторные запросы);
      • корректность обработки edge-case сценариев (нет котировок, задержки, частичная недоступность сервисов);
      • базовые проверки производительности критичных операций (быстрая подгрузка портфеля, лента котировок).
  • Тестовая стратегия и подход:

    • Разработка и поддержка тестовой документации:
      • high-level тестовые сценарии для ключевых бизнес-процессов;
      • чек-листы/регресс-матрицы под релизные циклы;
      • фиксация граничных условий и рисковых зон (денежные переводы, сделки с реальными деньгами).
    • Использование risk-based подхода:
      • приоритизация тестов вокруг денежных операций, корректности расчетов и безопасности.
    • Работа с тестовыми данными:
      • подготовка реалистичных сценариев: разные типы клиентов, статусы KYC, лимиты, валюты, удержания комиссий и налогов.
  • Инструменты и практика:

    • Мобильное тестирование:
      • Android: тесты на реальных устройствах и эмуляторах;
      • проверка разных версий ОС и экранов, push-уведомлений, deeplink-ов.
    • API-тестирование:
      • Postman/Insomnia/HTTP-клиенты для ручной проверки;
      • автоматизация (если релевантно для собеседования на Go): написание сервисных тестов на Go для API.

    Пример простого API-теста на Go для проверки критичного эндпоинта:

    package main

    import (
    "encoding/json"
    "net/http"
    "testing"
    )

    type PortfolioResponse struct {
    AccountID string `json:"account_id"`
    Balance float64 `json:"balance"`
    Currency string `json:"currency"`
    }

    func TestGetPortfolio(t *testing.T) {
    resp, err := http.Get("https://api.bank.test/v1/portfolio?account_id=123")
    if err != nil {
    t.Fatalf("request failed: %v", err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
    t.Fatalf("unexpected status: %d", resp.StatusCode)
    }

    var data PortfolioResponse
    if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
    t.Fatalf("decode failed: %v", err)
    }

    if data.AccountID != "123" {
    t.Errorf("unexpected account id: %s", data.AccountID)
    }
    if data.Currency != "RUB" {
    t.Errorf("unexpected currency: %s", data.Currency)
    }
    }
  • Взаимодействие с командой:

    • Плотная работа с разработчиками мобильного клиента и backend-сервисов:
      • участие в разборе инцидентов, логировании, уточнении требований, API-контрактов;
      • предложении улучшений UX и обработки ошибок.
    • Взаимодействие с аналитиками и продуктом:
      • уточнение бизнес-логики инвестпродуктов;
      • контроль, что поведение приложения соответствует регуляторным требованиям и ожиданиям пользователей.
    • Участие в релизном цикле:
      • участие в регрессе перед релизом;
      • проверка критичного функционала в релизных сборках;
      • формирование go/no-go рекомендаций.

Такой ответ демонстрирует:

  • понимание домена (финансы, инвестиции, риски);
  • умение выстраивать тестовую стратегию;
  • умение работать с API, данными и окружениями;
  • влияние на надежность и безопасность продукта, а не только факт «тестировал мобильное приложение».

Вопрос 3. Почему ты занимался только тестированием Android-версии приложения и не тестировал iOS?

Таймкод: 00:10:04

Ответ собеседника: правильный. Тестировал только Android, так как имел под рукой Android-устройства и Windows, а технических условий для тестирования iOS не было.

Правильный ответ:
Корректный и достаточно зрелый ответ опирается не на личные предпочтения, а на организационные и технические ограничения.

Кратко по сути:

  • Наличие инфраструктурных ограничений:
    • Для iOS-разработки и тестирования нужны устройства под iOS и, как правило, macOS (Xcode, симуляторы, сборка, дебаг).
    • Если в команде нет выделенного оборудования или доступа к нему, полноценно покрывать iOS невозможно.
  • Разделение зон ответственности внутри команды:
    • Команда могла быть разделена по платформам: один специалист отвечает за Android, другой — за iOS.
    • Это позволяет глубже погружаться в особенности платформы (различия в UI-гайдах, поведении push-уведомлений, разрешениях, сетевых ограничениях).
  • Практические причины:
    • CI/CD и тестовые среды могли быть настроены в первую очередь под Android.
    • Доступ к iOS-сборкам мог быть ограничен (Enterprise-сертификаты, распределение через TestFlight, политика безопасности).

Важно при ответе добавить, что:

  • Понимаешь особенности кросс-платформенного тестирования:

    • Критичные бизнес-потоки (регистрация, логин, операции с деньгами, инвестиционные сделки) должны быть консистентны между Android и iOS.
    • При наличии доступа к iOS-окружению ты бы:
      • сверял поведение и интерфейс между платформами;
      • проверял единообразие API-ответов, обработку ошибок и сценариев отказа;
      • участвовал в регрессе обеих платформ для ключевых сценариев.
  • Готов при наличии инфраструктуры:

    • Освоить инструменты под iOS (Xcode, симуляторы, device-farm, специфичные логи);
    • Встроить iOS в существующую стратегию тестирования и автоматизации.

Такой ответ показывает:

  • осознанность (это не «не хотел», а «не было ресурсов/зоны ответственности»);
  • понимание требований к iOS-тестированию;
  • готовность покрывать обе платформы при наличии технических условий.

Вопрос 4. Как была организована команда тестирования на проекте и почему завершилось твое участие?

Таймкод: 00:10:22

Ответ собеседника: правильный. В проекте было около 10 тестировщиков, по одному на модуль; в его команде было двое. Из-за сокращения бюджета оставили коллегу с устройствами под обе платформы, а его контракт завершили, так как он покрывал только Android.

Правильный ответ:
На такой вопрос важно кратко и структурировано описать:

  • как организованы роли и зоны ответственности в команде тестирования;
  • как устроено взаимодействие с кросс-функциональными командами;
  • дать прозрачное и спокойное объяснение завершения участия без конфликтов и негативных формулировок.

Сильный вариант ответа:

  • Организация команды тестирования:

    • Команда тестирования была встроена в продуктовые/фича-команды.
    • Всего примерно 10 специалистов по качеству, распределенных по модулям:
      • каждый модуль (платежи, инвестиции, профиль, onboarding, поддержка и т.д.) имел своего ответственного за качество;
      • для сложных или критичных модулей могло быть 1–2 человека, чтобы обеспечить покрытие всех платформ или направлений.
    • Роль тестировщиков включала:
      • участие в планировании спринтов и refinement;
      • анализ требований и проектирование тестов до разработки;
      • тестирование фич на тестовых и предпроизводственных окружениях;
      • участие в регрессионных прогонах перед релизом;
      • взаимодействие с разработчиками, аналитиками, DevOps и поддержкой.
    • Вертикали ответственности:
      • функциональное тестирование ключевых бизнес-сценариев;
      • интеграционное тестирование между мобильным приложением и backend-сервисами;
      • частичное участие в автоматизации (API, UI, smoke/regression suites) там, где это было оправдано по ROI.
  • Взаимодействие внутри продуктовых команд:

    • В каждой команде:
      • backend-разработчики (часто на Go/Java/Kotlin и т.п.);
      • мобильные разработчики (Android/iOS);
      • тестировщик(и) как часть команды, а не внешняя функция.
    • Процессы:
      • CI/CD: автоматические сборки, smoke-тесты, nightly/regression;
      • code review, тестирование по pull request/feature branch;
      • быстрый цикл обнаружения и фикса багов.
  • Причина завершения участия:

    • Проект столкнулся с оптимизацией бюджета и ресурсами:
      • было принято решение сократить количество людей, а не сворачивать продукт.
      • приоритет отдали специалисту, который покрывал сразу две платформы (Android и iOS), имел доступ и к соответствующей технике.
    • Формулировка:
      • завершение сотрудничества связано с перераспределением ресурсов и оптимизацией бюджета, а не с качеством работы.
      • свои задачи выполнял, покрывал Android-направление, участвовал в релизах, передал все знания и артефакты (тест-кейсы, сценарии, чек-листы, особенности модулей).

Такой ответ демонстрирует:

  • понимание зрелых процессов обеспечения качества: встраивание в продуктовые команды, ответственность за модули, работу по спринтам, использование CI/CD;
  • умение спокойно и профессионально объяснить завершение контракта как организационное решение, а не личную проблему;
  • адекватное отношение к командной структуре и распределению зон ответственности.

Вопрос 5. Предоставляла ли компания тестовые устройства или доступ к фермам для мобильного тестирования?

Таймкод: 00:11:12

Ответ собеседника: правильный. Компания не предоставляла тестовые устройства и фермы и не оплачивала подобные ресурсы.

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

Ключевые моменты:

  • Хорошая практика в мобильных проектах:

    • Компания должна обеспечивать:
      • реальными устройствами разных производителей, версий ОС, экранов;
      • доступом к облачным фермам устройств (BrowserStack, AWS Device Farm, Firebase Test Lab и т.п.);
      • возможностью тестировать push-нотификации, deeplink-и, работу в фоне, сетевые деградации.
    • Это критично для:
      • проверки стабильности и производительности;
      • обнаружения платформенных багов;
      • проверки UX на разных размерах экранов;
      • тестирования edge-кейсов, специфичных для моделей и версий ОС.
  • Если компания не предоставляет устройства/ферму:

    • Возникают риски:
      • ограниченное покрытие платформ и версий ОС;
      • повышенная вероятность того, что пользователи столкнутся с багами на конкретных устройствах;
      • невозможность полноценно проверить iOS или редкие Android-устройства, если их нет у команды.
    • При этом важно показать зрелую позицию:
      • использовать доступные ресурсы максимально эффективно (эмуляторы, свои устройства);
      • аргументированно доносить до менеджмента необходимость инфраструктуры для тестирования;
      • фиксировать риски в релизной документации: какие устройства покрыты, какие нет.
  • Как это может выглядеть в более зрелой среде:

    • Стенд и pipeline:

      • CI запускает автоматические UI/API тесты на эмуляторах + части устройств фермы.
      • Часть smoke/regression-прогонов идет на реальных девайсах.
    • Пример: интеграция автотестов с фермой устройств (схематично, Go):

      package main

      import (
      "log"
      "net/http"
      "strings"
      )

      // Условный пример запуска тест-сессии на облачной ферме устройств через API

      func startDeviceFarmSession(device, appURL string) error {
      reqBody := strings.NewReader(`{
      "device": "` + device + `",
      "app_url": "` + appURL + `",
      "test_suite": "regression"
      }`)

      resp, err := http.Post("https://device-farm.example/api/run", "application/json", reqBody)
      if err != nil {
      return err
      }
      defer resp.Body.Close()

      if resp.StatusCode != http.StatusAccepted {
      return err
      }

      log.Println("Device farm session started for", device)
      return nil
      }

      func main() {
      if err := startDeviceFarmSession("android-12-pixel-5", "https://storage/app.apk"); err != nil {
      log.Fatalf("cannot start device farm session: %v", err)
      }
      }
  • Как лучше формулировать ответ на интервью:

    • Честно указать, что компания не предоставляла инфраструктуру и это ограничивало покрытие.
    • Показать, что понимаешь:
      • как должно быть организовано тестирование в идеале;
      • что отсутствие устройств — это управленческое и инфраструктурное ограничение, а не норма качества.
    • Отметить, что при возможности:
      • выступаешь за внедрение девайс-ферм;
      • готов участвовать в выборе решений и интеграции их в CI/CD.

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

Вопрос 6. Как ты использовал Android Studio при тестировании: запускал ли эмулятор и просматривал логи приложения?

Таймкод: 00:11:57

Ответ собеседника: неполный. Использовал Android Studio для запуска приложения в эмуляторе, но не работал с логами приложения и не знал о такой возможности; серверные логи получал через поддержку.

Правильный ответ:
Для качественного тестирования мобильного приложения недостаточно просто запускать его в эмуляторе. Важно эффективно использовать инструменты Android Studio и экосистемы, чтобы:

  • диагностировать проблемы;
  • проверять корректность запросов/ответов;
  • отслеживать ошибки, краши, логирование, работу с сетью.

Ключевые аспекты использования Android Studio в тестировании:

  1. Запуск и конфигурация эмуляторов:

    • Создание и настройка AVD (Android Virtual Device):
      • разные версии Android (например, 8–14);
      • разные размеры экранов, DPI, форм-факторы;
      • проверка поведения приложения в разных условиях.
    • Использование эмуляторов для:
      • быстрых smoke-проверок новых сборок;
      • воспроизведения дефектов;
      • тестирования локализаций, ориентаций экрана, ограничений по памяти и сети.
  2. Работа с логами (Logcat) — ключевой навык:

    • Logcat позволяет отслеживать:
      • ошибки приложения (Exception, stack trace, ANR, crash);
      • логи уровня debug/info/warn/error;
      • теги, добавленные разработчиками для бизнес-логики.
    • Практическое применение:
      • воспроизводишь баг → смотришь stack trace → определяешь компонент (экран, сервис, API).
      • проверяешь, действительно ли отправился запрос, какие параметры, не упали ли сериализация/десериализация.
    • Фильтрация:
      • по имени пакета приложения;
      • по уровню логирования;
      • по тегу (например, "AUTH", "PAYMENTS", "INVEST", "API").
    • Это:
      • ускоряет коммуникацию с разработчиками;
      • позволяет давать точные и воспроизводимые баг-репорты.

    Пример: как разработчик может логировать важные события (тестировщик должен уметь это читать):

    // Android-код
    Log.d("AUTH", "Login request started for userId=$userId")
    Log.e("AUTH", "Login failed", exception)

    Тестировщик в Logcat:

    • фильтрует по тегу AUTH;
    • видит, на каком шаге и с каким исключением произошел сбой.
  3. Анализ сетевых запросов:

    • Подходы:
      • Использовать встроенные логи клиента (если приложение логирует HTTP-запросы/ответы).
      • Использовать прокси-инструменты (Charles Proxy, Fiddler, mitmproxy) совместно с эмулятором:
        • перенаправление трафика через прокси;
        • анализ запросов (URL, headers, body, коды ответов);
        • проверка корректности обработки ошибок (4xx, 5xx).
    • В идеале:
      • тестировщик понимает структуру API и может проверить консистентность:
        • что отображаемые данные соответствуют данным из ответа сервера;
        • что ошибки показываются пользователю корректно.

    Пример простого Go-сервиса, который логирует входящие запросы (то, что полезно анализировать при тестировании):

    package main

    import (
    "log"
    "net/http"
    )

    func portfolioHandler(w http.ResponseWriter, r *http.Request) {
    userID := r.URL.Query().Get("user_id")
    log.Printf("GET /portfolio user_id=%s", userID)

    // Тестировщик по логам может проверить, что запрос дошел с нужными параметрами
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"balance": 100000, "currency": "RUB"}`))
    }

    func main() {
    http.HandleFunc("/portfolio", portfolioHandler)
    log.Println("Server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
    }
  4. Отладка проблем производительности и стабильности:

    • Использование инструментов Android Studio:
      • Profiler для анализа потребления CPU, памяти, сети;
      • отслеживание утечек памяти, долгих операций на UI-потоке;
      • анализ ANR и crash-репортов.
    • В тестировании:
      • запуск длительных сценариев (scroll, список операций, лента котировок);
      • проверка, не растет ли память, не падает ли приложение при долгом использовании.
  5. Подход к ответу на интервью:

    • Стоит показать, что:
      • умеешь и считаешь обязательным работать с Logcat;
      • понимаешь важность логов для воспроизведения и локализации дефектов;
      • можешь, при необходимости, использовать прокси и анализировать сетевой трафик;
      • готов самостоятельно углубляться в инструменты, а не ждать подсказок.

Краткая, сильная формулировка:

  • Использую Android Studio не только для запуска приложения на эмуляторе, но и:
    • читаю логи через Logcat (ошибки, stack trace, бизнес-события);
    • анализирую сетевые запросы и ответы;
    • проверяю стабильность и производительность;
    • помогаю разработчикам быстро локализовать и воспроизводить проблемы.

Такой уровень работы с инструментом сильно повышает качество тестирования и ценность специалиста в команде.

Вопрос 7. Что ты делаешь при неожиданном вылете мобильного приложения и корректно ли игнорировать невоспроизводимые вылеты?

Таймкод: 00:13:31

Ответ собеседника: неполный. Пытается воспроизвести вылет, фиксирует шаги и видео; если воспроизвести не удаётся — ничего не делает. Позже признаёт, что это неправильно и что нужно смотреть логи, но не умеет их снимать.

Правильный ответ:
Неожиданный вылет приложения (crash) — один из самых критичных видов дефектов в мобильных продуктах, особенно в финансовых/банковских/инвестиционных системах. Игнорировать даже «редкие» или «неустойчиво воспроизводимые» вылеты нельзя. Важно уметь:

  • максимально зафиксировать контекст;
  • собрать дополнительную техническую информацию;
  • корректно эскалировать проблему, даже если стабильного сценария воспроизведения нет.

Подход к работе с вылетами:

  1. Базовый алгоритм действий при краше:

    • Сразу:
      • зафиксировать:
        • точное время вылета;
        • устройство/эмулятор (модель, версия ОС);
        • версию приложения;
        • тип сети (Wi-Fi/4G/нет сети, VPN);
        • окружение (dev/test/prod, тестовый/боевой пользователь);
      • записать:
        • последовательность действий вплоть до вылета (step-by-step);
        • видео (если возможно), чтобы разработчик видел поведение интерфейса и тайминги.
    • Попробовать воспроизвести:
      • повторить те же шаги;
      • варьировать данные (другой аккаунт, другая сумма, другой сценарий, другая сеть);
      • проверить на другом устройстве/эмуляторе;
      • проверить на другой версии приложения, если релевантно.
  2. Работа с логами — обязательная практика: Даже если ты не разработчик, умение собирать и прикладывать логи — ключевая часть зрелого подхода.

    Основные источники:

    • Logcat (через Android Studio или adb):
      • фильтрация по пакету приложения;
      • поиск по словам "Exception", "Fatal", "Crash", "ANR".
    • Серверные логи:
      • корреляция по времени и user_id/trace_id/request_id;
    • Встроенные crash-репортеры:
      • Firebase Crashlytics, Sentry, Bugsnag и т.п.

    Пример: как снять логи через adb при вылете (то, что должен уметь тестировщик):

    # Фильтр по пакету приложения
    adb logcat | grep com.example.app

    Или более точечно:

    adb logcat *:E

    После воспроизведения:

    • сохраняем фрагмент логов вокруг момента падения;
    • прикладываем к задаче.
  3. Что делать с невоспроизводимыми вылетами: «Невоспроизводимый» не значит «несуществующий». Правильное поведение:

    • Если воспроизвести не удалось:
      • все равно заводим баг (как минимум с пометкой для наблюдения/triage):
        • подробно описываем:
          • контекст (см. выше);
          • любые подозрения: асинхронные события, слабая сеть, push-уведомление, экран блокировки, смена языка, смена сети и т.п.;
          • прикладываем видео, скриншоты, логи (если хоть что-то есть).
      • маркируем как:
        • «требуется дополнительный анализ», «sporadic crash», «needs monitoring».
    • Просим:
      • подключить crash-репортер, если его нет;
      • по signature (stack trace) найти похожие вылеты у реальных пользователей или на других инсталляциях.

    Игнорировать такие вылеты:

    • неправильно с точки зрения качества;
    • дорого: один «редкий» вылет может бить по критическим сценариям (например, вылет при подтверждении операции или пополнении счета).
  4. Почему нельзя останавливаться только на шагах/видео: Только описание шагов без логов:

    • часто недостаточно для локализации;
    • увеличивает время анализа у разработчиков;
    • может привести к решению «Cannot reproduce» без реального расследования.

    Зрелый подход:

    • минимум: шаги + контекст + видео;
    • лучше: + клиентские логи (Logcat/adb), + id сессии/пользователя для поиска в серверных логах;
    • идеал: наличие crash-репортинга, где по stack trace и event-данным можно увидеть первопричину.
  5. Пример полезного бага по крашу (структура):

    • Заголовок:
      • "Crash при открытии экрана портфеля после восстановления из фона (Android 13, Pixel 6)"
    • Описание:
      • Шаги:
        1. Авторизоваться пользователем X.
        2. Открыть экран портфеля.
        3. Свернуть приложение на 5–10 минут.
        4. Вернуться в приложение через список последних.
      • Фактический результат:
        • Приложение вылетает.
      • Ожидаемый результат:
        • Приложение восстанавливает состояние без вылета.
    • Окружение:
      • Android 13, Pixel 6, версия приложения 1.2.3 (build 456), стенд: test.
    • Вложения:
      • видео;
      • фрагмент Logcat с stack trace (NullPointerException в PortfolioFragment);
      • timestamp.
  6. Связь с backend и Go-кодом: Краш часто может быть следствием некорректного ответа backend-а (пустые поля, неожиданный формат, 500-ошибка без обработки).

    Пример проблемного Go-обработчика, который может приводить к нестабильному поведению клиента:

    func portfolioHandler(w http.ResponseWriter, r *http.Request) {
    userID := r.URL.Query().Get("user_id")
    if userID == "" {
    // Плохая практика: неструктурированная ошибка
    w.WriteHeader(http.StatusInternalServerError)
    w.Write([]byte("error"))
    return
    }

    // ...
    }

    Клиент без защитной логики может не ожидать такой ответ и падать на парсинге. Зрелый подход на backend:

    • всегда возвращать предсказуемый JSON с кодом ошибки и сообщением;
    • логировать проблемные запросы;
    • это существенно упрощает диагностику вылетов.

Итоговая позиция:

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

Вопрос 8. Какие данные, помимо шагов и видео, нужно указывать при описании дефекта вылета приложения?

Таймкод: 00:16:58

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

Правильный ответ:
Для инцидентов с вылетами приложения важно описывать дефект так, чтобы разработчик мог максимально быстро:

  • понять контекст проблемы;
  • воспроизвести её локально;
  • увидеть первопричину по логам и коду.

Ограничиваться одной строкой и видео — недостаточно. Нужен структурированный багрепорт. Минимальный набор данных для краша/вылета:

  1. Заголовок:

    • Кратко и по сути, с контекстом:
    • Примеры:
      • "Crash при открытии экрана 'Портфель' после авторизации (Android 13)"
      • "Вылет при подтверждении перевода после смены сети Wi-Fi→4G (iOS 16)"
  2. Окружение: Уточняет, где и на чем наблюдается проблема:

    • устройство:
      • модель (Pixel 6, Samsung S21, Xiaomi 11 и т.п.);
      • версия ОС (Android 12/13/14, iOS 15/16/17).
    • тип устройства:
      • реальное / эмулятор.
    • версия приложения:
      • номер версии и build (например, 3.5.1 (build 457));
    • окружение/стенд:
      • dev, test, stage, prod, preprod и т.п.
    • сеть:
      • Wi-Fi/4G/5G/VPN/прокси, переключения сети;
    • доп. контекст:
      • включен/выключен VPN;
      • регион/язык системы, если релевантно.
  3. Точное время и частота:

    • когда произошел вылет (timestamp);
    • единичный случай или повторяется;
    • если повторяется:
      • примерная частота: 2 из 5 попыток, каждый раз при таком сценарии, один раз в день и т.д.
  4. Фактический результат:

    • кратко и конкретно:
      • "Приложение закрывается без сообщения об ошибке."
      • "После нажатия 'Подтвердить' приложение вылетает на рабочий стол."
      • "Появляется системное окно 'Приложение остановлено'."
  5. Ожидаемый результат:

    • четко, без расплывчатости:
      • "Операция завершается успешно, пользователь видит экран подтверждения."
      • "Приложение не вылетает, отображается сообщение об ошибке с предложением повторить."
  6. Технические данные (максимально важный блок):

    Даже если ты не пишешь код, ты должен уметь это запросить/снять.

    • клиентские логи:
      • Logcat (Android) или лог из Xcode/Crashlytics/Sentry/Bugsnag;
      • фрагмент вокруг момента краша (stack trace, тип исключения, теги).
    • идентификаторы для корреляции:
      • user_id / session_id / request_id / trace_id (если есть);
      • это позволяет backend-разработчикам найти инцидент в логах Go-сервиса, базы данных, очередей.
    • crash-отчет:
      • если подключен Crashlytics/Sentry:
        • прикрепить ссылку на конкретный инцидент или stack trace.

    Пример корректно приложенного stack trace (фрагмент):

    FATAL EXCEPTION: main
    Process: com.example.app, PID: 12345
    java.lang.NullPointerException: Attempt to invoke virtual method 'getBalance()' on a null object reference
    at com.example.app.portfolio.PortfolioFragment.render(PortfolioFragment.kt:58)

    Такой фрагмент сразу говорит разработчику, где смотреть.

  7. Дополнительные условия:

    • состояние приложения:
      • только что установили / обновили с предыдущей версии;
      • восстановление из фона;
      • смена темы, языка, аккаунта;
    • действия в параллели:
      • приход push-уведомления;
      • входящий звонок;
      • смена сети;
      • блокировка/разблокировка экрана.
    • наличие root/jailbreak (если релевантно политике безопасности).
  8. Пример хорошо оформленного бага на вылет:

    • Заголовок:
      • "Crash при открытии портфеля после восстановления приложения из фона (Android 13, Pixel 6)"
    • Окружение:
      • Device: Pixel 6
      • OS: Android 13
      • App: 3.5.1 (build 457)
      • Env: STAGE
      • Network: Wi-Fi, без VPN
    • Шаги:
      1. Авторизоваться под тестовым пользователем T123.
      2. Открыть экран "Портфель".
      3. Свернуть приложение на 10 минут.
      4. Вернуться в приложение из списка последних.
    • Фактический результат:
      • Приложение вылетает на рабочий стол без сообщения.
    • Ожидаемый результат:
      • Экран "Портфель" успешно открывается, приложение не вылетает.
    • Логи:
      • timestamp: 2025-05-01 10:23:45

      • Logcat (фрагмент):

        FATAL EXCEPTION: main
        java.lang.NullPointerException: portfolio == null
        at com.example.app.portfolio.PortfolioFragment.render(PortfolioFragment.kt:58)

    Уже из этого видно: проблема в обработке null-данных после восстановления.

  9. Связь с backend (Go) и устойчивостью: Если backend возвращает неожиданные данные/ошибки, приложение не должно падать.

    Пример устойчивого обработчика на Go (то, что ожидается с серверной стороны):

    type Portfolio struct {
    Positions []Position `json:"positions"`
    }

    func GetPortfolio(w http.ResponseWriter, r *http.Request) {
    userID := r.URL.Query().Get("user_id")
    if userID == "" {
    w.WriteHeader(http.StatusBadRequest)
    w.Write([]byte(`{"error":"missing user_id"}`))
    return
    }

    portfolio, err := loadPortfolio(userID)
    if err != nil {
    // Структурированная ошибка вместо "сломанных" данных
    w.WriteHeader(http.StatusInternalServerError)
    w.Write([]byte(`{"error":"internal_error"}`))
    return
    }

    // Даже если у пользователя нет позиций, возвращаем корректный объект
    if portfolio == nil {
    portfolio = &Portfolio{Positions: []Position{}}
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(portfolio)
    }

    Если тестировщик видит, что клиент падает на пустом/нестандартном ответе — это отличный кейс для репорта: и к мобильным, и к backend.

Вывод:

  • Грамотное описание дефекта вылета — это:
    • шаги + видео +
    • подробное окружение +
    • четкий expected/actual +
    • технические артефакты (логи, id, время).
  • Такой подход позволяет быстро локализовать и исправить критические дефекты и демонстрирует глубокое понимание процесса обеспечения качества.

Вопрос 9. Предпринимал ли ты попытки улучшить процессы тестирования и оформления багов на прошлом проекте?

Таймкод: 00:17:39

Ответ собеседника: неполный. Обсуждал проблемы только с коллегой, инициатив по системным изменениям почти не проявлял, объяснял это слабой компетенцией руководства.

Правильный ответ:
На такой вопрос важно показать не позицию «наблюдателя» или «жертвы процессов», а умение в рамках своей зоны влияния:

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

Даже если руководство слабое или сопротивляется, зрелый специалист демонстрирует инициативу, а не пассивность.

Оптимальная стратегия и примеры корректного поведения:

  1. Локальные инициативы в своей команде (минимум, который должен быть): Даже без формальной поддержки сверху можно:

    • Ввести у себя базовый стандарт создания багрепортов:
      • структура: заголовок, окружение, шаги, ожидаемый/фактический результат, вложения (логи, видео).
      • использовать шаблоны задач в трекере (Jira, YouTrack, Trello и т.п.).
    • Начать прикладывать:
      • логи (клиентские и при возможности серверные);
      • временные метки;
      • идентификаторы пользователей/запросов;
      • корректную приоритизацию (Severity/Impact).
    • Делать это последовательно, даже если от тебя формально «не требуют».
    • Показать на реальных примерах, что такие баги правятся быстрее и качественнее — это лучший аргумент.
  2. Инициирование улучшений на уровне команды/проекта: Вместо жалоб на «некомпетентность» менеджмента, правильный подход:

    • Предложить конкретные улучшения:

      • шаблон баг-репортов;
      • чек-листы по регрессии;
      • минимальные критерии приемки (Definition of Done/Ready);
      • обязательное указание версии приложения, стенда, устройства, ОС.
    • Оформлять инициативы в конструктивном виде:

      • короткий документ/страницу в Confluence/Notion: «Как мы оформляем баги, чтобы их чинили быстрее»;
      • пример 3–5 хорошо оформленных задач с быстрым временем реакции.
    • Не навязывать «сверху вниз», а показывать пользу:

      • меньше времени на уточнение деталей;
      • меньше нерелевантных вопросов от разработчиков;
      • меньше нерепродуцируемых багов.
  3. Работа с руководством и «сложными» менеджерами:

    • Важно не переводить разговор в плоскость «они некомпетентны», а выстраивать диалог через:
      • риск: "Сейчас у нас нет нормального оформления багов, из-за этого критические дефекты могут уходить в прод."
      • метрики: "Если структурировать баги, мы сократим время анализа и количество возвратов задач."
    • Формулировки:
      • не: «Вы всё делаете неправильно»;
      • а: «Есть несколько небольших изменений, которые повысят скорость и качество. Давайте попробуем на одном модуле/спринте».
  4. Примеры конкретных улучшений, которые можно было (и нужно) предлагать:

    • Шаблон баг-репорта в трекере:
      • поля:
        • Environment (env/stage),
        • App version,
        • Device/OS,
        • Steps,
        • Expected result,
        • Actual result,
        • Attachments (video/logs/screenshots),
        • Severity/Priority.
    • Внедрение минимального процесса triage:
      • регулярный разбор критичных багов;
      • классификация по приоритетам;
      • договоренность, что без ключевых полей баг возвращается на доработку.
    • Инициатива по логам и crash-репортингу:
      • предложить подключить Crashlytics/Sentry;
      • объяснить, как это снизит время поиска причин вылетов.
  5. Связь с backend и Go-контекстом (как «продать» улучшения технарям): Например, можно инициировать:

    • Согласованный формат логирования и trace-id, чтобы проще связывать:
      • запросы мобильного клиента;
      • логи Go-сервисов;
      • инциденты на фронте.

    Пример (Go):

    func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    traceID := r.Header.Get("X-Request-ID")
    if traceID == "" {
    traceID = generateTraceID()
    }

    // Логируем с traceID
    log.Printf("trace_id=%s method=%s path=%s", traceID, r.Method, r.URL.Path)

    // Прокидываем дальше
    ctx := context.WithValue(r.Context(), "trace_id", traceID)
    next.ServeHTTP(w, r.WithContext(ctx))
    })
    }

    Тестировщик, предлагая использовать X-Request-ID и указывая его в баг-репортах, помогает быстро находить нужные логи на backend.

  6. Как это сформулировать на интервью:

    Вместо:

    • «Я ничего не делал, потому что руководство некомпетентное»,

    Лучше сказать, что:

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

Такой ответ демонстрирует:

  • проактивность и ответственность за качество не только кода, но и процессов;
  • умение действовать конструктивно в сложной среде;
  • ориентацию на результат, а не на оправдания.

Вопрос 10. Приведи примеры использования снифера (Fiddler): для чего ты его применял и какие функции использовал.

Таймкод: 00:20:06

Ответ собеседника: правильный. Использовал Fiddler для перехвата запросов и анализа проблем, а также для подмены JSON-ответов через breakpoints, чтобы проверять отображение элементов и поведение интерфейса без изменения БД и без готового бэкенда; приводит примеры с портфелем акций и персональным менеджером.

Правильный ответ:
Использование HTTP-прокси (Fiddler, Charles, mitmproxy и аналогов) — один из ключевых инструментов для глубокого тестирования клиент–серверных приложений. Важно уметь применять его не только для «посмотреть запросы», но и для:

  • валидации корректности интеграции клиента и backend;
  • тестирования edge-case сценариев;
  • негативных сценариев и ошибок;
  • проверки устойчивости UI к любым вариантам ответов.

Ключевые, технически зрелые сценарии использования Fiddler:

  1. Анализ запросов и ответов (основа):

    • Проверка, что мобильное/веб-приложение:
      • ходит на правильные endpoint-ы;
      • использует корректные HTTP-методы (GET/POST/PUT/DELETE и т.п.);
      • отправляет обязательные заголовки (авторизация, content-type, trace-id);
      • передает корректные параметры (user_id, account_id, фильтры, пагинация).
    • Проверка ответов:
      • статус-коды (200, 201, 400, 401, 403, 404, 500 и т.п.);
      • структура JSON соответствует контракту (schema, типы, nullable-поля);
      • данные в UI соответствуют данным из API.

    Это позволяет:

    • быстро находить рассинхроны между фронтом и бэкендом;
    • отличать баг клиента от бага сервера.
  2. Тестирование негативных и пограничных сценариев через подмену ответов: Использование breakpoints / autoresponder / modifying on the fly:

    • Подмена тела успешного ответа:
      • Проверка, как фронт обрабатывает:
        • пустые массивы (нет позиций в портфеле, нет транзакций);
        • большие массивы (много записей);
        • null-ы, отсутствующие поля, неожиданные значения.
    • Подмена кодов ответа:
      • 400/422 — валидационные ошибки: корректно ли UI показывает сообщение;
      • 401/403 — просроченный токен/нет прав: происходит ли разлогин, обновление токена, информирование пользователя;
      • 404 — нет ресурса: не падает ли экран, есть ли fallback;
      • 500/502/504 — серверные ошибки и таймауты: есть ли retry, дружелюбное сообщение, отсутствие краша.
    • Подмена бизнес-данных:
      • наличие персонального менеджера/нет менеджера;
      • разные типы тарифов, лимитов, статусов KYC;
      • сложные кейсы: блокированный счет, маржинальные позиции, дефолт по облигации и т.п.

    Это особенно ценно:

    • когда backend-функционал еще не реализован или сложен в настройке;
    • когда нельзя/неудобно менять данные в базе.
  3. Тестирование устойчивости клиента к некорректным данным: Fiddler позволяет эмулировать то, что рано или поздно произойдет в реальной системе.

    • Примеры:
      • обязательное поле отсутствует;
      • тип поля не соответствует контракту (строка вместо числа);
      • очень длинные строки в имени, описании;
      • спецсимволы, emoji, RTL-тексты;
      • дублирующиеся ключи, лишние поля.
    • Цель:
      • убедиться, что приложение:
        • не падает (нет крашей);
        • корректно обрабатывает частичные данные;
        • показывает пользователю понятные сообщения.
  4. Диагностика проблем авторизации и безопасности:

    • Проверка:
      • как клиент добавляет токен (Bearer, cookies, custom headers);
      • что происходит при истечении токена: запрос на refresh, разлогин, сообщения.
    • Негатив:
      • подмена токена на некорректный;
      • удаление заголовка Authorization;
      • проверка, что backend корректно отвечает 401/403, а клиент правильно реагирует.
    • Базовые security-проверки:
      • убедиться, что чувствительные данные не уходят в открытом виде;
      • отсутствие логина/пароля в query-параметрах;
      • отсутствие персональных данных в URL и логах.
  5. Эмуляция сетевых проблем: В некоторых инструментах (Charles/mitmproxy; Fiddler частично через rules/latency):

    • добавление задержек (latency) к ответам;
    • обрыв соединения;
    • нестабильная сеть.
    • Цель:
      • проверить поведение клиента:
        • спиннеры, повторные попытки, timeouts;
        • нет ли фризов/крашей при плохой сети.
  6. Взаимодействие с backend и Go-сервисами: При тестировании Go-бэкенда через Fiddler можно:

    • Проверять соответствие API-реализации спецификации (OpenAPI/Swagger):
      • правильные поля, типы, коды ответов.
    • Воспроизводить запросы, которые делает клиент:
      • фиксировать их в Fiddler;
      • повторять и модифицировать руками;
      • искать баги в обработчиках.

    Пример простого Go-обработчика, под который удобно гонять запросы через Fiddler:

    package main

    import (
    "encoding/json"
    "log"
    "net/http"
    )

    type Portfolio struct {
    ClientID string `json:"client_id"`
    Assets []string `json:"assets"`
    }

    func portfolioHandler(w http.ResponseWriter, r *http.Request) {
    clientID := r.URL.Query().Get("client_id")
    if clientID == "" {
    w.WriteHeader(http.StatusBadRequest)
    _ = json.NewEncoder(w).Encode(map[string]string{
    "error": "missing client_id",
    })
    return
    }

    resp := Portfolio{
    ClientID: clientID,
    Assets: []string{"AAPL", "GOOGL", "TSLA"},
    }

    w.Header().Set("Content-Type", "application/json")
    if err := json.NewEncoder(w).Encode(resp); err != nil {
    log.Printf("encode error: %v", err)
    }
    }

    func main() {
    http.HandleFunc("/api/portfolio", portfolioHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
    }

    Через Fiddler:

    • можно подменять ответы этого сервиса (например, пустые Assets или 500-ошибку);
    • проверять, что фронт не ломается и корректно отображает состояние портфеля.
  7. Как это правильно подать на интервью: Сильный ответ должен показать, что ты:

    • используешь Fiddler не только «посмотреть трафик», но и:
      • валидируешь контракты;
      • тестируешь edge-cases и ошибки;
      • ускоряешь тестирование фич до готовности backend;
      • помогаешь разработчикам воспроизводить и локализовывать дефекты.
    • понимаешь ценность:
      • подмены ответов;
      • анализа заголовков;
      • проверки безопасности;
      • работы с нестабильной сетью.

Такой подход демонстрирует глубокое понимание клиент–серверного взаимодействия и умение использовать инструменты сниффинга как часть полноценной стратегии тестирования.

Вопрос 11. Какие элементы HTTP-ответа ты изменял при тестировании через Fiddler и как именно это делал?

Таймкод: 00:23:25

Ответ собеседника: правильный. В основном подменял тело ответа (body) через breakpoints, иногда используя шаблоны Fiddler; при подменах статус-код также корректно менялся.

Правильный ответ:
При использовании Fiddler (или аналогичных инструментов) важно понимать, что можно и нужно управлять не только телом ответа, но и статус-кодами, заголовками и задержками. Это позволяет полноценно тестировать устойчивость клиента, корректность обработки ошибок и соответствие API-контракту.

Ключевые элементы HTTP-ответа, которые полезно модифицировать:

  1. Тело ответа (Body) Основной и ожидаемый сценарий.

    Для чего:

    • Проверка отображения различных наборов данных без изменения БД.
    • Тестирование edge-case сценариев:
      • пустые списки;
      • большое количество элементов;
      • специфичные бизнес-состояния (заблокированный счет, персональный менеджер, разные статусы заявок);
      • невалидные или нестандартные значения (0, отрицательные, очень большие числа, редкие валюты).

    Как:

    • Включить breakpoints (Before Responses).
    • Изменить JSON в окне редактора:
      • добавить/удалить поля;
      • изменить значения;
      • эмулировать разные состояния.
    • Нажать Run to Completion, чтобы отправить модифицированный ответ клиенту.

    Пример: Было:

    {
    "hasPersonalManager": false
    }

    Сделали в Fiddler:

    {
    "hasPersonalManager": true,
    "managerName": "Иван Иванов",
    "managerPhone": "+7-900-000-00-00"
    }

    Проверяем: UI корректно отрисовывает блок менеджера.

  2. Статус-код (Status Code) Критично для проверки обработки ошибок и устойчивости клиента.

    Для чего:

    • Проверить реакцию приложения на:
      • 400/422 — ошибки валидации;
      • 401/403 — проблемы авторизации/прав;
      • 404 — отсутствующий ресурс;
      • 500/502/503/504 — внутренние/сетевые ошибки.

    Как:

    • На breakpoint в Response:
      • изменить oSession.responseCode = 500; или через интерфейс;
      • при необходимости скорректировать тело под формат ошибки.
    • Проверить:
      • не падает ли клиент;
      • показывает ли корректное сообщение;
      • выполняет ли retry там, где требуется.

    Пример негативного ответа:

    {
    "error": "internal_error",
    "message": "Service temporarily unavailable"
    }
  3. Заголовки (Headers) Часто игнорируются, но очень важны.

    Для чего:

    • Проверка:
      • кэширования (Cache-Control, ETag);
      • CORS (Access-Control-Allow-Origin);
      • контента (Content-Type: application/json / text/html);
      • авторизации (WWW-Authenticate);
      • версионности API, feature-флагов.
    • Тест:
      • как клиент ведет себя при неправильном/отсутствующем Content-Type;
      • что будет, если приходит неожиданная кодировка или не тот MIME-тип.

    Как:

    • На breakpoint редактировать headers:
      • добавлять/удалять/менять значения.
    • Примеры:
      • удалить Content-Type: application/json и проверить, не ломается ли парсер;
      • поменять Cache-Control и убедиться, что клиент не кэширует чувствительные данные.
  4. Искусственные задержки и сбои (если инструмент позволяет) Даже если делается не напрямую в Fiddler, концептуально это обязательная часть зрелого тестирования.

    Для чего:

    • Проверить поведение:
      • при долгих ответах (slow API);
      • при обрыве соединения;
      • при частичных ответах.

    Если использовать FiddlerScript:

    • можно добавить задержку:
    if (oSession.HostnameIs("api.example.com")) {
    oSession["request-trickle-delay"] = "300";
    oSession["response-trickle-delay"] = "1000";
    }

    Проверяем:

    • не блокируется ли UI;
    • корректно ли отображаются лоадеры;
    • есть ли таймауты и обработка ошибок.
  5. Интеграция с backend-контрактами (на примере Go) При тестировании Go-сервисов через Fiddler важно проверять, что клиент:

    • корректно обрабатывает предусмотренные API-ответы;
    • не падает при неожиданных ответах.

    Пример ожидаемого обработчика на Go:

    type ErrorResponse struct {
    Error string `json:"error"`
    Message string `json:"message"`
    }

    func handler(w http.ResponseWriter, r *http.Request) {
    // ...
    if badRequest {
    w.WriteHeader(http.StatusBadRequest)
    json.NewEncoder(w).Encode(ErrorResponse{
    Error: "invalid_input",
    Message: "amount must be positive",
    })
    return
    }
    // ...
    }

    Через Fiddler можно:

    • подменить 200 на 400;
    • оставить тело без ожидаемых полей;
    • убедиться, что клиент:
      • не крашится;
      • показывает пользователю понятную ошибку.

Вывод:

  • Правильное использование Fiddler включает:
    • модификацию body;
    • управление статус-кодами;
    • работу с заголовками;
    • эмуляцию задержек и сбоев.
  • Это дает возможность полноценно тестировать:
    • устойчивость клиента;
    • корректность обработки ошибок;
    • соответствие API-контракту;
    • поведение UI в сложных, редких и пограничных сценариях без вмешательства в реальный backend и БД.

Вопрос 12. Есть ли у тебя опыт тестирования продуктовой аналитики (воронки, события) и знаний о системах для хранения аналитики?

Таймкод: 00:24:54

Ответ собеседника: неправильный. Говорит, что не тестировал аналитику и не знает, где она хранится.

Правильный ответ:
Тестирование продуктовой аналитики является неотъемлемой частью качества современного продукта. Ошибки в событиях аналитики приводят к неверным продуктовым решениям, искаженному A/B-тестированию, неправильной оценке эффективности фич и маркетинговых кампаний. Игнорировать это направление нельзя.

Ниже — системный подход к тестированию аналитики, который ожидается.

Основные понятия продуктовой аналитики:

  • События (events):
    • фиксируют действия пользователя: просмотр экрана, клик по кнопке, успешная/неуспешная операция, скролл до блока, выбор тарифа и т.п.
  • Параметры (properties):
    • дополнительные данные события: user_id, session_id, тип устройства, версия приложения, тариф, сумма операции, источник трафика и др.
  • Воронки:
    • последовательности шагов, которые пользователь должен пройти:
      • пример: установка → запуск → регистрация → KYC → пополнение счета → первая сделка.
  • Пользовательские атрибуты:
    • статические/долгоживущие характеристики: регион, тип клиента, сегмент, наличие персонального менеджера и др.

Что и как нужно тестировать:

  1. Соответствие требованиям/спецификации событий:

    • Обычно существует:
      • аналитическая спецификация или tracking plan (в Confluence/Notion/Docs), где описано:
        • какое событие отправляется;
        • при каком действии;
        • с какими параметрами;
        • в какой системе оно должно оказаться.
    • Тестирование:
      • для каждого ключевого сценария (регистрация, логин, платеж, сделка, отмена, ошибка) проверить:
        • событие отправляется;
        • имя события соответствует спецификации;
        • все обязательные параметры присутствуют;
        • типы и значения параметров корректны;
        • нет лишних личных данных (GDPR/152-ФЗ — приватность).
  2. Целостность воронок:

    • Важно проверять не только отдельные события, но и их последовательность.
    • Примеры:
      • онбординг: app_openview_onboardingskip_onboarding или complete_onboarding;
      • инвестиции: open_portfolioopen_instrumentcreate_orderorder_success / order_failed.
    • Тестирование:
      • пройти реальный сценарий и убедиться, что:
        • все события пришли;
        • нет разрывов;
        • параметры связывают шаги одной сессии/пользователя (user_id, session_id).
  3. Проверка корректности параметров:

    • Критично для аналитики и сегментаций.
    • Примеры параметров:
      • user_id, account_id — должны совпадать с реальным тестовым пользователем;
      • screen_name, feature_flag, ab_group, tariff, device_model, os_version;
      • бизнес-параметры: суммы, валюты, типы операций, результаты (success/fail, причина отказа).
    • Ошибки:
      • пустые/0 значения;
      • неверные типы;
      • неправильные юнит-экономические поля → искаженная аналитика.
  4. Где и как смотреть аналитику:

    В реальных проектах используются системы:

    • продуктовая аналитика:
      • Amplitude, Mixpanel, AppMetrica, Firebase Analytics, AppsFlyer, Adjust и др.
    • хранилища/BI:
      • ClickHouse, BigQuery, Snowflake, Redshift, PostgreSQL, Vertica;
      • поверх них: Looker, Power BI, Tableau, Metabase, Superset.

    Подход к тестированию:

    • На уровне клиента:
      • смотреть запросы аналитики через:
        • Fiddler/Charles/mitmproxy;
        • логирование в консоль/Logcat;
        • debug-режим SDK (у многих аналитических SDK есть).
    • На уровне бэкенда/хранилища:
      • проверять, что события корректно попадают в целевую систему.
      • выполнять выборки по тестовому user_id/session_id.

    Пример простой проверки события в ClickHouse (SQL):

    SELECT event_name,
    user_id,
    properties["screen"],
    properties["result"],
    event_time
    FROM analytics_events
    WHERE user_id = 'test_user_123'
    AND event_time >= now() - INTERVAL 1 HOUR
    ORDER BY event_time;

    Здесь:

    • проверяем наличие нужных событий и параметров для тестового пользователя.
  5. Проверка приватности и безопасности аналитики:

    • Важно убедиться, что:
      • в аналитику не утекают:
        • полные номера карт;
        • CVV;
        • пароли;
        • паспортные данные;
        • избыточные персональные данные.
    • Тестирование:
      • просматриваем события;
      • убеждаемся, что используются id/токены/маскирование там, где необходимо.
  6. Связь с backend и Go-примерами:

    Часто аналитика реализуется на backend или проксируется через него.

    Пример простого события на Go:

    type AnalyticsEvent struct {
    Name string `json:"name"`
    UserID string `json:"user_id"`
    Properties map[string]string `json:"properties"`
    Timestamp time.Time `json:"timestamp"`
    }

    func TrackEvent(e AnalyticsEvent) {
    // Здесь могла бы быть отправка в Kafka / ClickHouse / сторонний сервис
    log.Printf("analytics: name=%s user=%s props=%v ts=%s",
    e.Name, e.UserID, e.Properties, e.Timestamp.Format(time.RFC3339))
    }

    func HandleOrderSuccess(userID, orderID string) {
    // бизнес-логика...
    TrackEvent(AnalyticsEvent{
    Name: "order_success",
    UserID: userID,
    Properties: map[string]string{
    "order_id": orderID,
    "source": "mobile",
    },
    Timestamp: time.Now(),
    })
    }

    Что должен проверить тестировщик:

    • событие order_success реально отправляется при успешном заказе;
    • user_id и order_id корректны;
    • событие содержит source=mobile для правильной сегментации.
  7. Как это правильно озвучить на интервью:

    Сильный ответ включает:

    • Да, тестировал:
      • корректность отправки событий на ключевых действиях;
      • структуру payload-ов аналитики;
      • соответствие tracking plan-у;
      • целостность воронок.
    • Использовал:
      • прокси-сниферы для перехвата событий;
      • доступ к аналитической БД или кабинетам систем (Amplitude/Firebase/AppMetrica и т.п.);
      • SQL-запросы для точечной проверки.
    • Понимаю:
      • влияние аналитики на продуктовые решения;
      • важность корректности и полноты данных;
      • требования к приватности и безопасности данных в аналитике.

Если практического опыта не было, корректная позиция:

  • продемонстрировать понимание вышеописанного подхода;
  • подчеркнуть готовность и способность быстро подключиться к тестированию аналитики;
  • не оставлять эту область «за бортом» как что-то второстепенное.

Вопрос 13. Какой у тебя опыт тестирования push-уведомлений и какие особенности необходимо проверять?

Таймкод: 00:26:10

Ответ собеседника: неполный. Проверял получение push-уведомлений при достижении цены акций, корректность текста и переход на нужный экран из трёх состояний приложения (активно, свернуто, выгружено), а также что уведомления не запрещены в настройках. Не углублялся в типы пушей, механику разрешений, причины неприхода, часть вопросов отдавал другим командам.

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

Основные аспекты, которые необходимо учитывать и тестировать.

Понимание цепочки доставки push-уведомлений:

Типичная схема:

  • Backend-приложения (часто Go-сервисы) формируют событие (например, сработал триггер по цене/ордера).
  • Сервис уведомлений:
    • для Android: FCM (Firebase Cloud Messaging);
    • для iOS: APNs;
    • иногда используется свой gateway или сторонний провайдер.
  • На устройстве:
    • системный сервис получает push;
    • передает приложению (в зависимости от типа и состояния);
    • приложение отображает уведомление, обрабатывает клик, выполняет deep link и т.п.

Тестировщик должен:

  • понимать эту цепочку логически;
  • уметь сузить область проблемы (backend, провайдер, токен, настройки OS, логика клиента).

Типы push-уведомлений и что тестировать:

  1. Транзакционные и критичные:

    • примеры:
      • подтверждение операции;
      • исполнение/отмена ордера;
      • безопасность (вход с нового устройства, смена пароля).
    • требования:
      • всегда доставляются корректно;
      • содержат точную информацию;
      • ведут на правильный экран (детали операции/ордера);
      • без орфографических, юридических и смысловых ошибок.
  2. Информационные:

    • новости, статусы заявок, изменения условий, напоминания.
    • проверяем:
      • не спамят ли;
      • корректная персонализация;
      • актуальность контента.
  3. Маркетинговые:

    • акции, предложения, кросс-продажи.
    • проверяем:
      • соблюдение opt-in/opt-out;
      • корректную сегментацию;
      • соответствие настройкам пользователя.
  4. Silent/Background push:

    • содержат данные для фонового обновления без UI-уведомления.
    • проверяем:
      • не появляются в шторке;
      • изменения данных происходят корректно;
      • не приводят к лишним пробуждениям, батарейному дрену, крашам.

Ключевые сценарии тестирования push-уведомлений:

  1. Состояния приложения: Обязательно проверяются все варианты:

    • активно (foreground);
    • свернуто (background);
    • полностью выгружено (killed). Для каждого:
    • уведомление приходит;
    • отображается ожидаемым образом (шторка/баннер/внутренний баннер в приложении);
    • клик по уведомлению:
      • открывает нужный экран;
      • корректно обрабатывает данные (id ордера, инструмента, операции).
  2. Deep link / навигация:

    • Проверить:
      • переход по пушу:
        • из активного приложения;
        • из бекграунда;
        • из полностью закрытого.
      • корректное открытие:
        • конкретного экрана (ордер, портфель, чат, акция);
        • с правильными параметрами (order_id, ticker, campaign_id).
    • Важно:
      • отсутствие «битых» экранов;
      • отсутствие крашей при открытии по устаревшему/невалидному payload-у.
  3. Текст и контент:

    • Проверяем:
      • локализацию (язык системы/приложения);
      • формат дат, сумм, валют;
      • отсутствие чувствительных данных (номер карты целиком, паспорт, CVV);
      • длина текста в разных устройствах (не обрезается ключевая информация);
      • соответствие юридическим требованиям, если уведомление про финансы.
  4. Настройки уведомлений: Нужно различать:

    • Настройки ОС:
      • пользователь мог отключить уведомления для приложения;
      • проверяем корректное поведение:
        • если пуши отключены — уведомлений нет;
        • приложение может информировать о необходимости включить.
    • Настройки внутри приложения:
      • категории уведомлений:
        • торговые сигналы;
        • операции;
        • маркетинг;
        • безопасность.
      • проверяем:
        • включение/отключение отдельных типов работает корректно;
        • изменения сохраняются и учитываются backend-ом;
        • пользователь не получает уведомления по отключенным категориям.
  5. Причины неприхода пушей, которые нужно понимать: Даже если тестировщик не управляет инфраструктурой, он должен уметь диагностировать.

    Основные причины:

    • неверный/устаревший device token;
    • смена устройства или переустановка приложения;
    • отключены уведомления на уровне ОС;
    • агрессивная оптимизация батареи/фоновой работы;
    • сетевые проблемы;
    • ошибки на backend (не отправили, неверный payload, превышен лимит);
    • блок со стороны FCM/APNs (некорректный ключ, сертификат).

    Практика:

    • проверять логи backend-а и провайдера (если есть доступ);
    • сверять, был ли пуш отправлен и с каким статусом;
    • проверять device token в логах/кабинете.

Пример логики отправки push из backend на Go (упрощенно):

type PushPayload struct {
Title string `json:"title"`
Body string `json:"body"`
Type string `json:"type"` // e.g. "order_executed"
Data map[string]string `json:"data"`
}

func SendPushToFCM(token string, payload PushPayload) error {
body, _ := json.Marshal(map[string]interface{}{
"to": token,
"notification": map[string]string{
"title": payload.Title,
"body": payload.Body,
},
"data": payload.Data,
})

req, _ := http.NewRequest("POST", "https://fcm.googleapis.com/fcm/send", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "key="+os.Getenv("FCM_SERVER_KEY"))

resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
b, _ := io.ReadAll(resp.Body)
return fmt.Errorf("fcm error: %s", string(b))
}

return nil
}

Что важно тестировать с точки зрения клиента:

  • корректность payload-а:
    • type, data.order_id, data.ticker и др. приходят и используются;
  • устойчивость:
    • если payload частично битый — приложение не падает.

SQL-пример проверки регистрации устройства для пушей:

SELECT user_id, device_id, push_token, platform, updated_at
FROM push_subscriptions
WHERE user_id = 'test_user_123';

Здесь тестировщик проверяет:

  • зарегистрирован ли токен;
  • та ли платформа;
  • обновляется ли запись при переустановке приложения.

Как это оформить на интервью:

Сильный ответ должен показать, что ты:

  • проверяешь пуши:
    • во всех состояниях приложения;
    • с корректной навигацией и deep link-ами;
    • с учетом пользовательских и системных настроек;
  • понимаешь причины, по которым пуши могут не приходить, и умеешь сузить область:
    • токен, настройки, сеть, backend, провайдер;
  • следишь за:
    • корректностью текста и локализации;
    • безопасностью (без утечек чувствительных данных);
    • правильной категоризацией (opt-in/opt-out для маркетинга);
  • при необходимости:
    • смотришь сетевой трафик (Fiddler/Charles) для проверки отправки/получения;
    • используешь логи и аналитические системы для валидации.

Такой подход демонстрирует глубокое понимание экосистемы push-уведомлений, а не только факт «пришло/не пришло».

Вопрос 14. Когда и как приложение должно запрашивать разрешение на отправку push-уведомлений?

Таймкод: 00:29:31

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

Правильный ответ:
Корректное понимание механики разрешений на push-уведомления критично для тестирования. Поведение различается между платформами (iOS/Android) и эволюционирует с версиями ОС, поэтому важно знать базовые принципы и типичные практики.

Ключевые моменты:

  1. iOS: явный запрос разрешения обязателен
  • На iOS:

    • Приложение не имеет права показывать push-уведомления, пока пользователь явно не дал согласие.
    • Разработчик вызывает системный диалог через API (UNUserNotificationCenter).
    • Пользователь видит нативный системный prompt:
      • «Приложение "X" хочет отправлять вам уведомления».
    • Варианты: Разрешить / Не разрешать.
  • Типичный UX-подход (best practice):

    • Не показывать системный диалог сразу при первом запуске «в лоб».
    • Сначала показать in-app pre-prompt:
      • объяснить ценность уведомлений: «Будем присылать алерты о сделках, исполнении ордеров, важных событиях по портфелю».
      • если пользователь согласен → вызвать системный диалог;
      • если отказался → не дергать системный диалог, чтобы не получить перманентный «deny».
  • Что тестировать:

    • системный диалог появляется в корректный момент (не спамит, не срывает критичные сценарии);
    • при выборе «Allow»:
      • статус в настройках iOS → включен;
      • приложение корректно регистрирует push token;
    • при выборе «Don’t Allow»:
      • приложение не получает токен;
      • нет пушей;
      • в интерфейсе:
        • корректные подсказки, как включить уведомления через системные настройки.
  1. Android: различия по версиям и типам уведомлений

Исторически:

  • До Android 13:
    • Push-уведомления через FCM фактически работали без отдельного системного разрешения, если приложение было установлено.
    • Пользователь мог отключить уведомления в настройках системы вручную.
  • Начиная с Android 13 (Tiramisu):
    • Введено явное runtime-разрешение POST_NOTIFICATIONS.
    • Поведение стало ближе к iOS:
      • при первом использовании уведомлений приложение запрашивает разрешение;
      • пользователь может разрешить или запретить.

Что важно при тестировании на Android:

  • Для Android 13+:
    • Проверить:
      • корректный запрос runtime-разрешения в подходящий момент;
      • сценарии «разрешил/запретил»;
      • корректную работу при последующих запусках (нет повторного спама диалогами);
      • корректную реакцию UI, если уведомления запрещены.
  • Для более старых версий:
    • Проверить:
      • поведение включенных по умолчанию уведомлений;
      • реакцию приложения на ручное отключение уведомлений в системных настройках.
  1. Где и как проверять разрешения:

Проверка на уровне платформы:

  • iOS:
    • Настройки → Уведомления → Выбрать приложение:
      • Включены ли уведомления;
      • Типы (баннеры, звуки, бейджи).
  • Android:
    • Настройки приложения → Уведомления:
      • Включены ли уведомления;
      • Категории (notification channels) — финансовые алерты, маркетинг, сервисные события.

Тестовые сценарии:

  • Установить приложение → запустить:
    • убедиться, что:
      • iOS: нет скрытого обхода — пуши не приходят без разрешения;
      • Android 13+: корректно запрашивается разрешение.
  • Изменить настройки уведомлений в системе:
    • отключить → убедиться, что пуши не приходят;
    • включить обратно → пуши снова приходят.
  • Проверить in-app настройки:
    • переключение категорий уведомлений внутри приложения:
      • влияет на факт и типы приходящих push-уведомлений;
      • корректно синхронизируется с backend (подписки/темы).
  1. Взаимодействие с backend и токенами

При выдаче разрешения:

  • Приложение:
    • запрашивает push token у FCM/APNs;
    • отправляет его на backend, где хранится связка user_id ↔ device_id ↔ token.
  • При отзыве разрешения или логауте:
    • токен должен быть обновлен/удален;
    • тестировщик проверяет корректность этого поведения.

Пример упрощенного backend-кода на Go:

type PushToken struct {
UserID string `json:"user_id"`
DeviceID string `json:"device_id"`
Token string `json:"token"`
Platform string `json:"platform"` // "ios" / "android"
UpdatedAt time.Time `json:"updated_at"`
}

func RegisterPushToken(w http.ResponseWriter, r *http.Request) {
var t PushToken
if err := json.NewDecoder(r.Body).Decode(&t); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}

// upsert токена в БД
// ...
w.WriteHeader(http.StatusNoContent)
}

SQL-проверка корректной регистрации:

SELECT user_id, device_id, token, platform, updated_at
FROM push_tokens
WHERE user_id = 'test_user_123';

Что должен проверить тестировщик:

  • токен создается/обновляется при включении уведомлений;
  • токен удаляется/обнуляется при логауте/отказе от уведомлений (если так задумано);
  • нет «мертвых» токенов, которые могут вести к невалидным попыткам отправки.
  1. Типичные ошибки, которые нужно уметь замечать:
  • Пуши работают только потому, что на тестовом устройстве уже давно сохранен токен, а сценарий первого запуска/первого запроса прав не протестирован.
  • Отсутствует корректный UX вокруг разрешений:
    • нет объяснения пользователю, зачем нужны уведомления;
    • приложение не обрабатывает отказ (молча ведет себя так, как будто пуши включены).
  • Некорректная логика:
    • пуши продолжают отправляться на токены пользователей, которые отозвали разрешения или удалили приложение.
  • Отсутствие кросс-платформенной консистентности:
    • разные тексты, разные сценарии, отсутствуют единые принципы безопасности и приватности.

Итоговая позиция:

  • Зрелый ответ должен показывать:
    • понимание различий iOS/Android в механизме разрешений;
    • знание, что без явного согласия (особенно iOS, Android 13+) пуши работать не должны;
    • умение тестировать сценарии:
      • первый запрос;
      • повторный запуск;
      • смена настроек в системе;
      • включение/отключение внутри приложения;
    • связь с регистрацией токенов на backend и корректной маршрутизацией уведомлений.

Ответ вида «пуши просто по умолчанию включены» без понимания механики — это красный флаг, которого нужно избегать.

Вопрос 15. Что такое диплинки и как их правильно применять и тестировать?

Таймкод: 00:31:35

Ответ собеседника: неполный. Воспринимает диплинк как ссылку на конкретный экран приложения; использовал готовые ссылки от разработчиков и переходы через чат/мок в снифере. Не знает других способов вызова диплинков и технических деталей работы.

Правильный ответ:

Диплинки — ключевой механизм навигации и интеграции мобильного приложения с внешним миром: пуш-уведомления, веб-сайт, письма, баннеры, партнёрские ссылки, маркетинговые кампании, сценарии «вернуться в нужный экран по клику». Глубокое понимание диплинков важно как для тестирования UX, так и для проверки безопасности и корректности навигации.

Виды диплинков и базовая теория:

  1. Классические (custom scheme)

    • Формат: myapp://screen/portfolio?account_id=123
    • Приложение регистрирует собственную схему (myapp://).
    • Плюсы: просто.
    • Минусы:
      • не работают из браузера без доп. логики, конфликтуют между приложениями;
      • не поддерживают fallback по умолчанию (если приложение не установлено).
  2. HTTP/HTTPS диплинки

    • Формат: обычный URL, например:
      • https://app.example.com/portfolio?account_id=123
    • Используются:
      • как обычные web-ссылки;
      • могут перехватываться приложением.
    • Базис для:
      • Android App Links;
      • iOS Universal Links.
  3. Universal Links (iOS) и App Links (Android)

    • Привязка домена к приложению:
      • на стороне сервера размещается специальный файл с маппингом (apple-app-site-association, assetlinks.json);
      • ОС доверяет, что данный домен связан с конкретным приложением.
    • Если приложение установлено:
      • ссылка открывается сразу в приложении;
    • Если не установлено:
      • открывается в браузере (fallback).
    • Это стандарт для зрелых продуктов.

Ключевые сценарии использования диплинков:

  • Переходы из push-уведомлений:
    • клик по пушу ведет на:
      • экран сделки;
      • детали инструмента;
      • чат с менеджером;
      • конкретную акцию/кампанию.
  • Переходы из email/SMS/баннеров:
    • восстановление доступа;
    • подтверждение почты/телефона;
    • спецпредложения.
  • Интеграции с вебом и партнёрами:
    • промо-ссылки, UTM-метки, трекинг кампаний.
  • Внутренние переходы:
    • из webview в нативные экраны;
    • из чата/сообщений/FAQ — по ссылке сразу к нужному действию.

Что и как нужно тестировать (структурно):

  1. Корректность навигации:

    • Для каждого диплинка:
      • открывает ли он нужный экран:
        • когда приложение:
          • не запущено (cold start);
          • в фоне;
          • активно.
      • правильно ли обрабатываются параметры:
        • order_id, ticket, account_id, campaign_id.
    • Если данные некорректны:
      • показывается ли внятная ошибка/заглушка;
      • нет ли крашей.
  2. Состояние авторизации:

    • Критический момент, особенно для финансовых приложений.

    Сценарии:

    • Пользователь не авторизован:
      • по диплинку на защищенный экран:
        • должен попасть на экран логина;
        • после успешного логина — редирект на целевой экран (deep link должен быть «пронесён»).
    • Пользователь авторизован:
      • переход сразу на целевой экран без лишних шагов.
    • Тестировать:
      • диплинк на публичный контент (например, промо-страница) — должен открываться без авторизации;
      • диплинк на чувствительные данные — только через авторизованную сессию.
  3. Обработка невалидных/устаревших диплинков:

    • Нельзя допускать:
      • пустой экран,
      • краш,
      • некорректные данные.
    • Тест:
      • диплинк с несуществующим order_id;
      • диплинк с битым форматом параметров;
      • диплинк на старую фичу.
    • Ожидаемое поведение:
      • понятное сообщение;
      • безопасный fallback (например, на главный экран или список сущностей).
  4. Интеграция с пушами и маркетингом:

    • Проверить, что:
      • payload пуша содержит корректный диплинк/данные для навигации;
      • клик из пуша:
        • открывает нужный экран;
        • учитывает состояние авторизации;
        • корректен на всех поддерживаемых платформах.
    • Для маркетинговых кампаний:
      • диплинк поддерживает UTM-метки / campaign_id;
      • не ломает навигацию из-за дополнительных параметров.
  5. Безопасность диплинков:

    • Нельзя:
      • передавать в открытом виде чувствительные данные:
        • полный номер карты, пароли, токены, паспорт.
    • Нельзя:
      • позволять через диплинк выполнять опасные действия без подтверждения:
        • автоподтверждение операции;
        • изменение настроек;
        • перевод средств.

    Тестировщик должен проверять:

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

Практические способы тестирования диплинков:

  1. Ручной запуск:

    • Через adb (Android):

      adb shell am start -W -a android.intent.action.VIEW -d "myapp://portfolio?account_id=123"
    • Через терминал/браузер/Notes на iOS:

      • ввести myapp://... или https://app.example.com/... и перейти.
  2. Через сниферы/моки:

    • Можно подменять URL-ы в Fiddler/Charles,
    • но важно уметь запускать диплинки напрямую, без зависимости от заранее вшитых ссылок.
  3. Автотесты:

    • UI-тесты (Espresso/XCUITest/Appium/Detox), которые:
      • запускают приложение по диплинку;
      • проверяют правильность открытого экрана.

Пример backend-логики на Go, связанной с диплинками:

  • Генерация безопасной ссылки для подтверждения действия:
func GenerateConfirmLink(userID, actionID string) string {
token := signAction(userID, actionID) // HMAC/JWT
return "https://app.example.com/confirm?action_id=" + actionID + "&token=" + token
}

Тестировщик должен проверить:

  • что без валидного token действие не выполняется;
  • диплинк ведет:
    • в приложение (если установлено),
    • в браузер с корректным UX (если нет приложения).

Итоговая позиция для интервью:

Сильный ответ должен показать, что ты:

  • понимаешь:
    • виды диплинков (custom scheme, universal/app links);
    • связь диплинков с пушами, письмами, вебом;
    • влияние авторизации и безопасности;
  • умеешь тестировать:
    • навигацию из разных состояний приложения;
    • обработку параметров;
    • ошибки и невалидные ссылки;
    • поведение при отсутствии приложения (fallback);
  • используешь:
    • прямой вызов диплинков (adb, URL-схемы),
    • сниферы/моки — как дополнение, а не единственный способ.

Такой подход демонстрирует не только пользовательское понимание диплинков, но и техническое и системное видение их роли в архитектуре продукта.

Вопрос 16. Что должно происходить при использовании некорректной дипссылки и как правильно обрабатывать такие случаи?

Таймкод: 00:34:23

Ответ собеседника: неполный. Говорит, что при неверной дипссылке открывался чёрный экран, и это не считалось багом; далее признает, что корректнее показывать понятное сообщение об ошибке, а не пустой экран.

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

Ключевые принципы обработки некорректных диплинков:

  1. Никогда не оставлять пользователя с «черным экраном»

    • Недопустимые варианты поведения:
      • черный экран без элементов управления;
      • пустой экран;
      • бесконечный спиннер без объяснения;
      • краш приложения.
    • Почему это баг:
      • пользователь не понимает, что произошло;
      • теряется доверие, особенно в банковских/инвестиционных продуктах;
      • поведение не даёт возможности продолжить работу.
  2. Безопасный и понятный fallback При некорректной, устаревшей или частично битой дипссылке приложение должно:

    • Выполнить валидацию параметров диплинка:
      • обязательные параметры присутствуют;
      • формат корректный (числа, UUID, enum-значения и т.п.);
      • сущность существует (ордер, счёт, инструмент).
    • Если данные невалидны:
      • не делать «полупереход» в неизвестное состояние;
      • показать:
        • понятный экран с сообщением:
          • «Ссылка недействительна или устарела»
          • «Мы не нашли запрошенный объект»
        • и/или перенаправить:
          • на безопасный экран (главный, список счетов/портфелей, раздел помощи).
    • Важно:
      • не выполнять автоматически чувствительные действия (подтверждение операции и пр.);
      • не раскрывать лишнюю внутреннюю информацию в ошибке.
  3. Обработка разных типов проблем с диплинками:

    Основные категории:

    • Некорректная схема или путь:

      • myapp://unknown_screen или https://app.example.com/unknown.
      • Ожидаемое поведение:
        • общий fallback-экран или редирект на главную + информирование.
    • Неверные или отсутствующие параметры:

      • myapp://order?order_id= или order_id=abc вместо числа.
      • Ожидаемое:
        • валидация параметров;
        • если ключевой параметр невалиден — показать ошибку, не пытаться открыть «полупустой» экран.
    • Не существующая сущность:

      • диплинк на уже удаленный/несуществующий ордер/счет.
      • Ожидаемое:
        • запрос к backend;
        • при 404 или аналогичном сценарии:
          • сообщение «Объект не найден»;
          • предложение вернуться в список или на главный экран.
  4. Примеры корректного поведения (концептуально):

    • Пользователь перешел по битой ссылке из письма:
      • Приложение открывается → проверяет диплинк → не может найти сущность →
        • показывает экран:
          • «Ссылка недействительна или устарела»,
          • кнопка «На главный экран».
    • Партнер прислал диплинк с ошибкой в параметре кампании:
      • Приложение:
        • не падает,
        • не висит в пустоте,
        • показывает безопасный fallback без «ломаной» логики.
  5. Что должен проверять при тестировании диплинков:

    • Поведение при:
      • полностью некорректном URL;
      • корректном пути, но невалидных параметрах;
      • корректном формате, но несуществующих идентификаторах.
    • Варианты состояний:
      • приложение закрыто;
      • в фоне;
      • активно.
    • Наличие:
      • дружелюбного текста ошибки;
      • кнопок для продолжения работы;
      • отсутствия крашей и зависаний.
  6. Пример простой схемы обработки диплинков (псевдологика):

    На уровне клиента:

    • Распарсить диплинк.
    • Проверить:
      • что маршрут известен;
      • что обязательные параметры есть и валидны.
    • Если условие не выполняется:
      • не открывать «полупустой» экран;
      • показать fallback/ошибку.

    На уровне backend (если диплинк ведет на сущность):

    Пример: Go-обработчик, который корректно обрабатывает несуществующую сущность:

    func GetOrder(w http.ResponseWriter, r *http.Request) {
    orderID := r.URL.Query().Get("order_id")
    if orderID == "" {
    w.WriteHeader(http.StatusBadRequest)
    _ = json.NewEncoder(w).Encode(map[string]string{
    "error": "invalid_request",
    "message": "order_id is required",
    })
    return
    }

    order, err := loadOrder(orderID)
    if err == sql.ErrNoRows {
    w.WriteHeader(http.StatusNotFound)
    _ = json.NewEncoder(w).Encode(map[string]string{
    "error": "not_found",
    "message": "Order not found",
    })
    return
    } else if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    _ = json.NewEncoder(w).Encode(map[string]string{
    "error": "internal_error",
    "message": "Please try again later",
    })
    return
    }

    w.Header().Set("Content-Type", "application/json")
    _ = json.NewEncoder(w).Encode(order)
    }

    Клиент по такому ответу:

    • не падает,
    • показывает пользователю понятное сообщение,
    • при диплинке на несуществующий order_id — обрабатывает ситуацию корректно.
  7. Как это сформулировать на интервью:

    Сильная позиция:

    • Некорректная дипссылка не должна ломать приложение.
    • Обязательные требования:
      • валидация параметров;
      • устойчивость к ошибкам;
      • понятный UX (сообщение об ошибке + безопасный маршрут);
      • отсутствие черных экранов и крашей.
    • При тестировании:
      • целенаправленно проверяю некорректные и устаревшие диплинки;
      • считаю пустой/черный экран и «тихий фейл» полноценными багами.

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

Вопрос 17. Опиши структуру дипссылки: из каких основных частей она состоит.

Таймкод: 00:35:49

Ответ собеседника: неправильный. Не смог описать структуру дипссылки, упомянул только JSON c полем ссылки и признал, что не обращал внимания на формат.

Правильный ответ:

Диплинк по сути — это URL со строго определенной структурой, который используется для навигации в приложение (или на веб), передачи параметров и, при необходимости, безопасной идентификации контекста. Понимание структуры дипссылки важно для тестирования корректной маршрутизации, безопасности и интеграций.

Базовая структура любой дипссылки:

В общем виде диплинк (как и обычный URL) можно разложить на части:

scheme://host/path?query#fragment

Ключевые элементы:

  1. Схема (scheme)

    • Определяет, кто обрабатывает ссылку.
    • Варианты:
      • Custom scheme:
        • myapp://
        • пример: myapp://portfolio/123
      • HTTP/HTTPS:
        • https://app.example.com/...
        • используется для Universal Links (iOS) и App Links (Android).
    • Что важно:
      • custom-схема должна быть уникальной (чтобы не конфликтовать с другими приложениями);
      • https-ссылки позволяют использовать единый формат для веба и приложения.
  2. Хост (host)

    • Для custom-схемы:
      • может быть логическим пространством маршрутов:
        • myapp://trading/...
        • myapp://auth/...
      • иногда опускается, и сразу идет путь.
    • Для https-схем:
      • домен приложения или сервиса:
        • app.example.com
        • link.example.com
  3. Путь (path)

    • Описывает, какой экран или сущность нужно открыть.
    • Примеры:
      • myapp://portfolio
      • myapp://portfolio/123
      • https://app.example.com/orders/567
    • Логика:
      • сегменты пути часто кодируют:
        • тип экрана (portfolio, order, instrument, profile),
        • идентификатор сущности.
  4. Параметры запроса (query parameters)

    • Часть после ?.
    • Используются для передачи динамических данных.
    • Формат:
      • ?key=value&key2=value2
    • Примеры:
      • myapp://order?order_id=123&source=push
      • https://app.example.com/instrument?ticker=AAPL&campaign_id=SPRING2025
    • Для тестирования важно:
      • знать, какие параметры обязательны;
      • проверять валидацию типов и значений;
      • отслеживать, как они влияют на навигацию и состояние.
  5. Фрагмент (fragment, часть после #)

    • В вебе используется для якорей/внутренней навигации.
    • В мобильных диплинках применяется реже, но может использоваться как дополнительный контекст.
    • Пример:
      • https://app.example.com/help#faq_deposits
    • Если используется:
      • нужно проверить, что приложение корректно обрабатывает этот фрагмент.

Примеры корректно структурированных диплинков:

  1. Custom scheme:
  • myapp://portfolio?account_id=123
    • scheme: myapp
    • host: (отсутствует/логический)
    • path: /portfolio
    • query: account_id=123
  1. Universal/App Link:
  • https://app.example.com/order?order_id=987&source=push
    • scheme: https
    • host: app.example.com
    • path: /order
    • query:
      • order_id=987
      • source=push
  1. Сущность в path:
  • https://app.example.com/instrument/AAPL?source=email
    • path: /instrument/AAPL
    • query: source=email

Требования к хорошему диплинку:

  • Однозначная маршрутизация:
    • по ссылке понятно, какой экран и в каком контексте должен открыться.
  • Явные и документированные параметры:
    • есть спецификация: какие параметры, какие типы, какие обязательны.
  • Безопасность:
    • не передавать в диплинке:
      • пароли;
      • полные номера карт;
      • токены доступа;
      • персональные данные в открытом виде.
    • для критичных действий:
      • использовать защищенные токены и серверную валидацию (не доверять только содержимому ссылки).

Пример серверной генерации безопасного диплинка на Go:

func GenerateSecureDeepLink(userID, orderID string) string {
// Генерируем подписанный токен, который проверяется на backend
token := sign(userID, orderID) // HMAC/JWT
return "https://app.example.com/order?order_id=" + orderID + "&token=" + token
}

При тестировании:

  • Проверить:
    • структура соответствует спецификации;
    • обязательные параметры обрабатываются;
    • при отсутствии/некорректности параметров:
      • нет краша;
      • показывается понятная ошибка или выполняется безопасный fallback;
    • критичные действия не выполняются только на основании открытого параметра без серверной проверки.

Итого:

  • Дипссылка — это не «просто JSON с полем link», а полноценный URL с четкой структурой.
  • Глубокое понимание:
    • scheme + host + path + query (+ fragment)
    • позволяет:
      • осознанно тестировать навигацию;
      • находить баги в маршрутизации;
      • проверять безопасность и устойчивость при некорректных/злоумышленных входных данных.

Вопрос 18. Есть ли у тебя практический опыт работы с iOS как пользователем или при тестировании?

Таймкод: 00:36:38

Ответ собеседника: правильный. Почти не пользовался iOS и фактически не тестировал на этой платформе, за исключением единичных эпизодов.

Правильный ответ:
Для такого вопроса важен не только факт наличия/отсутствия опыта, но и понимание ключевых особенностей iOS, влияющих на тестирование. Даже если основной практический опыт связан с Android, нужно уметь:

  • осознанно переносить знания на iOS,
  • понимать платформенные отличия,
  • быстро адаптироваться под экосистему.

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

  1. Особенности iOS, важные для тестирования:
  • Модель разрешений:

    • Четко выраженный opt-in:
      • уведомления, геолокация, камера, микрофон, контакты, трекинг (ATT), фото и т.д.
    • Тестирование:
      • поведение при первом запросе;
      • сценарии «Allow» / «Don’t Allow»;
      • изменение разрешений через системные настройки;
      • реакция приложения при отсутствии нужного разрешения (корректные подсказки, отсутствие крашей).
  • Push-уведомления:

    • Нельзя отправлять пуши без явного согласия пользователя.
    • Используется APNs, особенности формата payload, delivery.
    • Проверка:
      • корректный запрос разрешения;
      • поведение пушей в состояниях foreground/background/killed;
      • навигация по пушу (deeplink/universal link).
  • Universal Links:

    • Предпочтительный механизм диплинков:
      • https://...-ссылки открывают приложение, если оно установлено, иначе — веб.
    • Тестирование:
      • корректная привязка домена (apple-app-site-association);
      • переходы из писем, браузера, мессенджеров;
      • fallback при отсутствии приложения.
  • Навигация и UX:

    • Стандартные паттерны:
      • таб-бар внизу, навигационный бар, свайпы назад;
    • Требование к нативности поведения:
      • важно проверять, что приложение не ломает привычные для iOS паттерны, если это критично для продукта.
  1. Практические аспекты тестирования iOS:

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

  • Xcode + iOS Simulator:
    • запуск сборок;
    • просмотр логов приложения;
    • тестирование диплинков:
      • открытие xcrun simctl openurl booted "scheme://...".
  • Чтение логов:
    • использование Console.app или логов Xcode для анализа крашей и ошибок.
  • Crash- и analytics SDK:
    • Firebase, Sentry, AppMetrica, etc. — аналогично Android, но с учетом iOS-специфики.
  1. Ожидаемая позиция на интервью:

Если реального коммерческого опыта мало, сильный ответ должен быть примерно таким по смыслу:

  • Осознанное признание:
    • "Основной боевой опыт — на Android, на iOS работал ограниченно."
  • Понимание отличий:
    • упоминание разрешений, Universal Links, APNs, особенностей push-логики, UX-паттернов.
  • Готовность быстро закрыть гэп:
    • "Понимаю архитектуру клиент–серверного взаимодействия, работу пушей, диплинков, аналитики; инструменты iOS (Xcode, симуляторы, логи) для меня понятны концептуально, при наличии доступа к технике быстро доучусь и встрою эти практики."

Важно показать:

  • что отсутствие широкой практики на iOS — вопрос текущих условий, а не принципиальной неспособности;
  • что базовые концепции мобильной разработки и тестирования переносимы между платформами;
  • что ты понимаешь, какие именно аспекты iOS нужно тестировать иначе, чем на Android (разрешения, universal links, политика безопасности, поведение фоновых задач и уведомлений).

Вопрос 19. Как протестировать экран с одним полем ввода и кнопкой, которая определяет, является ли введённое значение числом?

Таймкод: 00:37:06

Ответ собеседника: неполный. Предлагает позитивные кейсы с разными числами, рассуждает про длину поля и отсутствие спецификации, приводит единичные примеры (0, 9, число в кавычках), но не формирует системный набор тестов: нет покрытия границ, отрицательных и дробных значений, пробелов, спецсимволов, пустого ввода и т.п.

Правильный ответ:

Подход к этому вопросу должен показать умение:

  • быстро прояснить требования (что именно считается «числом»),
  • системно применить эквивалентное разбиение и анализ граничных значений,
  • не уйти в бесконечную «несформулированную спецификацию», а предложить разумный набор проверок,
  • думать как о фронте (валидация, UX), так и о бэкенде (реализация проверки).

Сначала — ключевые уточнения (устно/мысленно):

Перед проектированием тестов важно явно проговорить (или спросить):

  1. Что считается числом в контексте задачи:
    • Только целые неотрицательные? (0, 1, 123)
    • Целые со знаком? (-1, +5)
    • Дробные? (1.5, 0.0, 3.14, 1,5)
    • В какой нотации:
      • точка или запятая как разделитель?
      • экспоненциальная форма (1e10)?
  2. Какие ограничения:
    • максимальная длина ввода?
    • есть ли ограничения по диапазону?
  3. Какое ожидаемое поведение:
    • при валидном числе: показать «Да, это число», подсветить зеленым и т.п.
    • при невалидном: «Не число», подсветка, подсказка.
  4. Где выполняется проверка:
    • только на клиенте (JS/мобильный код);
    • на сервере (API);
    • оба варианта.

Если на интервью это не уточнили — стоит проговорить предположения и протестировать несколько сценариев трактовки.

Далее — системный набор тестов (предположим, что число = любое валидное десятичное число с возможным знаком и точкой).

  1. Позитивные кейсы (валидные числа):
  • Простейшие:
    • "0"
    • "5"
    • "9"
    • "10"
  • Многозначные:
    • "123456"
  • Со знаком:
    • "-1"
    • "+7"
  • Дробные:
    • "0.5"
    • "10.0"
    • "-3.14"
  • Экстремальные по длине/размеру:
    • очень длинное число в рамках допустимых требований (например, 20+ символов, если нет ограничений).
  • Допустимые пробелы (при условии, что тримим ввод):
    • " 123"
    • "123 "
    • " 3.14 "
    • Если по требованиям пробелы вокруг допускаются → после trim значение валидно.

Проверяем:

  • Корректно ли UI/логика определяет их как числа.
  • Нет ли падений при больших значениях (на фронте и на бэкенде).
  1. Негативные кейсы (не число):
  • Пустой ввод:
    • ""
    • " "
  • Буквы и алфавитно-цифровые:
    • "a"
    • "abc"
    • "123a"
    • "a123"
  • Спецсимволы:
    • "!"
    • "12!"
    • "#$%"
  • Число в кавычках:
    • "'123'"
    • ""123""
  • Смешанные:
    • "1 2" (внутренний пробел)
    • "12-3"
    • "++1"
    • "--1"
  • Неверный формат дробей:
    • "1.2.3"
    • "."
    • "-"
    • "+."
  • Локализация (если договорились, что только точка допустима):
    • "1,5" — должно считаться невалидным, если запятая не поддерживается.
  • Очень длинный мусор:
    • строка, превышающая ограничения длины, с символами.

Проверяем:

  • Корректно определяет как «не число»;
  • Сообщение об ошибке понятное;
  • Нет краша, нет зависаний.
  1. Граничные значения:

Даже для простой задачи важно показать мышление через границы.

  • Минимальные:
    • пустая строка;
    • "0" — частый edge-case.
  • Максимальные:
    • строка длиной = max_length - 1, max_length, max_length + 1.
    • Важно понять:
      • ограничивает ли поле ввод (UI) или сервер/валидация возвращает ошибку.
  • Пограничные форматы:
    • "-0"
    • "+0"
    • ".0" или "0."
    • решаем по требованиям, считать ли это валидным.
  1. UX и поведение:
  • Когда проверка выполняется:
    • по клику на кнопку;
    • по потере фокуса;
    • в реальном времени.
  • Что происходит:
    • при ошибке: подсказка рядом с полем, красная рамка, не падает страница.
    • при успехе: понятный результат.
  • Отсутствие side-effects:
    • повторное нажатие на кнопку;
    • очистка поля;
    • быстрый ввод/удаление.
  1. Кросс-платформенные и технические моменты:

Если есть серверная проверка:

  • Нужно протестировать согласованность фронта и бэкенда:
    • фронт считает "1e3" числом, а backend — нет (или наоборот) — это баг требований/контракта.
  • Использовать снифер (Fiddler/Charles) для проверки:
    • какие данные реально отправляются;
    • как backend отвечает.

Примеры:

Простой серверный обработчик проверки числа на Go:

func isNumberHandler(w http.ResponseWriter, r *http.Request) {
s := r.URL.Query().Get("value")

// Тримим пробелы
s = strings.TrimSpace(s)
if s == "" {
respond(w, false)
return
}

// Пробуем распарсить как float
if _, err := strconv.ParseFloat(s, 64); err != nil {
respond(w, false)
return
}

respond(w, true)
}

func respond(w http.ResponseWriter, ok bool) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]bool{"is_number": ok})
}

Кейс для тестировщика:

  • Проверить все вышеописанные значения против API:
    • "123" → is_number=true
    • "abc" → false
    • "1.2.3" → false
    • " 3.14 " → true
    • "" → false

SQL здесь не обязателен, так как задача не про хранение, но если бы сохраняли значения, нужно проверить:

  • что невалидные данные не попадают в таблицу;
  • что тип поля в БД (INT, NUMERIC, VARCHAR) соответствует логике.
  1. Как это компактно сформулировать на интервью:

Хороший устный ответ:

  • Коротко уточнить, что считаем числом.
  • Сказать:
    • применяю эквивалентное разбиение и граничные значения;
    • проверяю:
      • валидные целые и дробные;
      • знаки, пробелы, большие числа;
      • буквы, спецсимволы, пустой ввод, смешанные строки;
      • поведение UI и сообщений об ошибке;
      • согласованность фронт-/бэкенд-валидации, если она есть.
  • Не закапываться только в «нет спецификации», а показать, как из этого состояния вытащить четкий набор проверок.

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

Вопрос 20. Какие конкретные значения ты проверишь в поле, чтобы отличить числа от нечисловых данных, и какие результаты ожидаешь?

Таймкод: 00:45:57

Ответ собеседника: неполный. Приводит отдельные примеры (0, 9, "0", одиночная буква, пустой ввод, SQL-инъекции), частично рассуждает про сообщения об ошибках, но не даёт системного покрытия классов значений и ожидаемых результатов.

Правильный ответ:

Задача: есть одно поле ввода и кнопка, которая определяет, является ли введённая строка числом. Ожидается системный набор проверок, основанный на:

  • эквивалентном разбиении;
  • анализе граничных значений;
  • проверке типичных и нетривиальных вариантов ввода;
  • аккуратном учёте UX и безопасности.

Ниже — пример структурированного набора тестов (предположим, что число — это валидное десятичное число, допускающее знак и точку; если требования иные, часть кейсов переедет из «валидных» в «невалидные»).

  1. Базовые валидные значения

Проверяют «нормальные» числа без шума.

  • "0" → число
  • "5" → число
  • "9" → число
  • "10" → число
  • "123456" → число

Ожидаемый результат:

  • Флаг/сообщение: «Это число» (true / OK).
  • Нет ошибок, нет крашей.
  1. Знак числа

Показывают, что логика корректно обрабатывает + и -.

  • "-1" → число (если отрицательные допустимы)
  • "+1" → число (если плюс допускается)
  • "-0" → число (в зависимости от трактовки, обычно да)
  • "++1", "--1", "+-1" → не число

Ожидаемый результат:

  • Корректные варианты: «Это число».
  • Двойные и смешанные знаки: «Не число».
  1. Дробные числа

Проверяют поддержку десятичной части.

  • "0.0" → число
  • "3.14" → число
  • "-3.14" → число (если отрицательные допустимы)
  • ".5" / "5." → зависят от требований:
    • либо валидные,
    • либо «Не число» — важно явно зафиксировать.
  • "1.2.3" → не число

Ожидаемый результат:

  • Валидные форматы по договору: «Это число».
  • Множественные точки и странные комбинации: «Не число».
  1. Пробелы

Тестируют, выполняется ли trim() и как обрабатываются внутренние пробелы.

  • "123" → число
  • " 123" → если тримим, число.
  • "123 " → если тримим, число.
  • " 3.14 " → при trim, число.
  • "1 2" → не число (внутренний пробел).
  • " " (только пробелы) → не число.

Ожидаемый результат:

  • Внешние пробелы — по best practice игнорируются.
  • Внутренние пробелы — «Не число».
  • Пустая/пробельная строка:
    • «Не число» + понятное сообщение: «Введите значение».
  1. Буквы и смешанные значения

Проверяют отделение числового ввода от текстового.

  • "a" → не число
  • "abc" → не число
  • "123a" → не число
  • "a123" → не число
  • "12a34" → не число

Ожидаемый результат:

  • Всегда: «Не число».
  • Без крашей и некорректных преобразований.
  1. Специальные символы

Проверяют устойчивость к мусору и потенциальным инъекциям.

  • "!" → не число
  • "@" → не число
  • "1!" → не число
  • "$123" → не число
  • ""0"" / "'0'" (число в кавычках) → не число
  • "()123" → не число
  • "" → не число
  • "1 OR 1=1" → не число

Ожидаемый результат:

  • «Не число» + отсутствие краша/инъекций.
  • На уровне бэкенда:
    • строка обрабатывается безопасно;
    • нет выполнения SQL/JS;
    • фронт сообщает о некорректном значении.
  1. Пустой ввод и нулевые кейсы
  • "" (пустая строка) → не число
  • " " (пробелы) → не число

Ожидаемый результат:

  • Явное сообщение:
    • «Введите значение» или «Поле не может быть пустым».
  • Никаких падений, повторная проверка работает.
  1. Экзотика / дополнительные форматы (по согласованию)

Если система потенциально может поддерживать расширенные форматы — тестируем явно.

  • Экспоненциальная форма:
    • "1e3", "1E3" → либо число, либо «Не число», в зависимости от требований.
  • Локализация:
    • "1,5" → либо число, если запятая разрешена;
    • либо «Не число», если принимаем только точку.

Важно:

  • Сначала явно зафиксировать, поддерживаем ли мы эти форматы.
  • Если нет — всегда: «Не число».
  1. Граничные значения по длине

Проверяют поведение на очень длинных вводах.

Допустим, max длина = 20 символов (пример, надо спросить).

  • "12345678901234567890" → число (если не выходим за лимит и парсер выдерживает).
  • Строка длиной > max:
    • UI должен либо ограничивать ввод,
    • либо backend/валидация возвращает «Не число» / «Слишком длинное значение».

Ожидаемый результат:

  • Без переполнений, паник, 500-ошибок.
  • Прозрачное ограничение.
  1. Проверка согласованности фронта и бэка (если есть API)

Если кнопка дергает backend, тестируем связку:

Пример API на Go:

func isNumberHandler(w http.ResponseWriter, r *http.Request) {
input := strings.TrimSpace(r.URL.Query().Get("value"))
if input == "" {
respond(w, false)
return
}

if _, err := strconv.ParseFloat(input, 64); err != nil {
respond(w, false)
return
}

respond(w, true)
}

func respond(w http.ResponseWriter, ok bool) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]bool{"is_number": ok})
}

Тестировщик должен:

  • проверить все основные классы значений:
    • валидные → is_number: true;
    • невалидные (буквы, кавычки, SQL-мусор, пустые) → is_number: false;
  • убедиться, что нигде не происходит:
    • 500 без понятной причины;
    • SQL/код-инъекция;
    • отличающееся поведение фронта и бэка.

Вывод (как ответ на интервью):

  • Сначала уточняю, что считается числом.
  • Затем системно проверяю:
    • простые числа;
    • знаки;
    • дробные;
    • пробелы;
    • буквы и смешанные строки;
    • спецсимволы и потенциальные инъекции;
    • пустой ввод и длину.
  • Для каждого класса значений формулирую однозначное ожидание: «число» или «не число», плюс корректное сообщение об ошибке и отсутствие крашей.

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

Вопрос 21. Как протестировать поведение приложения при отсутствии или потере интернет-соединения при проверке числа в форме?

Таймкод: 00:51:16

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

Правильный ответ:

Корректный ответ должен показать:

  • понимание, где именно выполняется проверка (клиент или сервер),
  • различие между «нет сети», «медленная сеть», «сетевые ошибки сервера»,
  • ясные ожидания по UX: лоадеры, таймауты, сообщения, отсутствие «зависаний» и повторов «магическим образом».

Сначала зафиксируем архитектурный контекст.

  1. Ключевой уточняющий вопрос
  • Проверка «это число или нет» реализована:
    • только на клиенте (локальная валидация)?
    • на сервере (через API)?
    • комбинированно (клиент предварительно валидирует, затем отправляет на сервер для доп.проверки/логики)?

От этого зависит сценарий:

  • Если валидация целиком на клиенте:
    • отсутствие сети не должно вообще влиять на определение «число/не число».
  • Если валидация завязана на backend:
    • отсутствие/потеря сети — полноценный кейс, требующий внятного UX.

Ниже разберем оба подхода.

  1. Если проверка происходит на клиенте

В хорошо спроектированной системе:

  • Проверка числа:
    • выполняется локально (JS/мобильный код),
    • результат не зависит от сети.

Тестовые сценарии:

  • Отключить интернет полностью:
    • ввести валидное число → нажать кнопку → ожидаем мгновенный локальный результат «Число».
    • ввести нечисло → ожидаем «Не число».
  • Медленный интернет / флаппинг сети:
    • вообще не влияет на результат.
  • Важно:
    • отсутствие лишних лоадеров и ожиданий;
    • никаких запросов «просто чтобы проверить, число это или нет».

Вывод:

  • Если логика такая, UX не должен зависеть от сети.
  • Любой «зависимый от сети» детект числа в этом случае — архитектурный запах.
  1. Если проверка происходит на сервере (через API)

Допустим, кнопка отправляет введенное значение на backend, который возвращает is_number = true/false. Тогда нужно системно протестировать:

Состояния сети:

  • нет сети (offline);
  • сеть отвалилась в момент запроса;
  • очень медленная сеть (таймаут);
  • сеть есть, но сервер недоступен (5xx, connection refused).

3.1. Нет сети до нажатия кнопки

Сценарий:

  • Отключаем сеть (в режиме полета / через dev tools / через прокси).
  • Вводим значение.
  • Нажимаем кнопку проверки.

Ожидания:

  • Мгновенная реакция:
    • либо:
      • локальная проверка статуса сети и сразу понятное сообщение:
        • «Нет интернет-соединения. Проверьте подключение и повторите попытку.»
      • без бессмысленного лоадера;
    • либо:
      • быстрый fail запроса (ошибка соединения) с тем же сообщением.
  • Никаких:
    • бесконечных спиннеров;
    • подвисаний UI;
    • «магических» автопроверок спустя минуты.

3.2. Потеря сети во время запроса

Сценарий:

  • Включена сеть.
  • Вводим значение → нажимаем кнопку → запрос уходит.
  • Во время запроса отключаем сеть/дропаем соединение.

Ожидания:

  • По истечении разумного таймаута:
    • отображается сообщение о проблеме сети:
      • «Не удалось проверить из-за проблем с соединением»;
    • пользователь может:
      • повторить попытку вручную после восстановления сети.
  • Не должно быть:
    • автоматического «дожидания сети» и внезапного результата без действий пользователя;
    • зависания UI.

3.3. Медленная сеть (таймаут)

Сценарий:

  • Ограничиваем пропускную способность (например, через Charles/Fiddler/OS).
  • Нажимаем кнопку.

Ожидания:

  • Появляется лоадер (индикация ожидания).
  • По истечении таймаута:
    • запрос прерывается;
    • показывается понятное сообщение:
      • «Превышено время ожидания ответа. Попробуйте позже.»
  • Важно:
    • таймаут должен быть конечным и разумным (например, 5–15 секунд, но это часть спецификации/настройки);
    • лоадер должен исчезать, UI — оставаться отзывчивым.

3.4. Серверная ошибка при наличии сети

Сценарий:

  • Сеть есть.
  • Backend возвращает:
    • 500, 502, 503, 504.

Ожидания:

  • Пользователь видит:
    • сообщение о технической ошибке:
      • «Сервис временно недоступен»;
    • а не «это не число».
  • Деталь:
    • нельзя подменять сетевую/серверную ошибку на результат валидации.
    • различаем:
      • «введено не число» (корректная обработка),
      • «не удалось проверить» (техническая ошибка).
  1. Комбинированный подход (рекомендованный)

Оптимальная архитектура:

  • базовая проверка «число/не число» — локально;
  • сервер используется для более сложной логики, логирования, бизнес-правил.

Тогда:

  • При отсутствии сети:
    • локальная валидация все равно работает;
    • если нужно обратиться к серверу — отображается отдельное сообщение:
      • но результат «это число» / «не число» не зависит от сети.
  • Отличный вариант для UX и надежности.
  1. Инструменты и техника тестирования
  • Отключение сети:
    • режим полета;
    • отключение Wi-Fi/моб.данных;
    • dev tools (для веб);
    • сетевые профили в Charles/mitmproxy/Fiddler.
  • Эмуляция:
    • высокой задержки (latency);
    • потерь пакетов;
    • обрыва соединения.
  1. Пример серверной реализации на Go + ожидаемое поведение
func IsNumberHandler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
defer cancel()

value := r.URL.Query().Get("value")

select {
case <-ctx.Done():
// Таймаут / отмена — корректная техническая ошибка
http.Error(w, `{"error":"timeout"}`, http.StatusGatewayTimeout)
return
default:
value = strings.TrimSpace(value)
if value == "" {
respond(w, false)
return
}
if _, err := strconv.ParseFloat(value, 64); err != nil {
respond(w, false)
return
}
respond(w, true)
}
}

func respond(w http.ResponseWriter, ok bool) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]bool{"is_number": ok})
}

Тестировщик:

  • при нормальной сети:
    • получает корректный true/false.
  • при отключенной/потерянной сети:
    • видит обработанную ошибку сети/таймаута на клиенте, а не падение.
  • фронт:
    • отделяет сообщение «не число» от «не удалось проверить».
  1. Как компактно ответить на интервью

Сильный ответ в устной форме:

  • Сначала уточняю, где живет логика проверки.
  • Если проверка локальная — сеть не влияет, тестирую, что все работает offline.
  • Если есть запрос к backend:
    • проверяю:
      • отсутствие сети до клика (мгновенное понятное сообщение);
      • потерю сети во время запроса (таймаут + ошибка без зависаний);
      • медленную сеть (адекватный лоадер и ограниченный таймаут);
      • сетевые ошибки сервера (5xx) — отличаю их от «не число».
    • ожидаю:
      • никаких крэшей, вечных спиннеров или «магического» продолжения старого запроса;
      • только явное повторение пользователем после восстановления связи.

Такой подход демонстрирует понимание сетевой надежности, UX и корректного разделения технических и бизнес-результатов.

Вопрос 22. Что ещё нужно проверить в простом одностраничном приложении помимо логики формы проверки числа?

Таймкод: 00:55:18

Ответ собеседника: неполный. Упоминает установку/запуск/удаление приложения, сворачивание/разворачивание, негативные сценарии ввода и базовую защиту (SQL-инъекции). Не покрывает важные аспекты: устойчивость валидации, UX, локали, форматы, обработку ошибок, кроссплатформенность и пр.

Правильный ответ:

Даже если приложение выглядит “игрушечным” (одно поле + одна кнопка), зрелый подход требует проверить вокруг формы весь контекст качества: функциональность, устойчивость, UX, безопасность, производительность, платформенные особенности. Ниже — структурированный список, который можно адаптировать под веб, мобайл или десктоп.

  1. Функциональность формы (дополнение к уже обсужденной логике)

Не повторяя детально Вопросы 19–20:

  • Подтвердить:
    • корректную работу для всех классов входных данных:
      • валидные числа (целые, знаковые, дробные — согласно требованиям),
      • нечисловые строки,
      • пробелы, смешанные символы, спецсимволы.
  • Проверить:
    • единую и предсказуемую модель поведения (однотипное сообщение для всех невалидных значений или осмысленные категории);
    • отсутствие различий в логике между UI и backend:
      • фронт не должен говорить «число», если backend считает иначе (и наоборот).
  1. UX и удобство использования

Даже для простого экрана это критично.

Проверить:

  • Понятность интерфейса:
    • есть ли пояснение, что делает форма;
    • понятные тексты полей и кнопки;
    • понятные сообщения при нечисловом вводе:
      • «Введите число» vs бессмысленное «Error».
  • Мгновенность и предсказуемость реакции:
    • есть ли визуальный отклик по нажатию на кнопку (анимация/состояние disabled/лоадер);
    • нет ли задержек без индикации.
  • Обработка повторных действий:
    • многократные клики по кнопке;
    • быстрый ввод/удаление;
    • изменение значения после получения результата — корректно ли пересчитывается.
  • Доступность (Accessibility, если веб/мобайл):
    • фокус на поле ввода при открытии;
    • работа с клавиатурой (Tab, Enter);
    • корректная работа с экранными ридерами:
      • поле и кнопка имеют осмысленные labels;
      • сообщения об ошибках читаются.
  1. Валидация форматов, локали и раскладки

Даже в “простом” приложении это часто источник багов.

Проверить:

  • Локали:
    • как ведет себя ввод в разных языках/региональных настройках:
      • разделитель дробной части (точка против запятой);
      • цифры национальных алфавитов (арабские/индийские и т.п., если релевантно);
    • нет ли зависимости логики от языка интерфейса.
  • Раскладки:
    • случайный ввод букв из другой раскладки (например, русская «О» вместо латинской «0»);
  • Автозамены и автокоррекция (мобайл):
    • отключают ли они корректность ввода чисел;
    • нет ли неожиданной подстановки.
  1. Состояния приложения и жизненный цикл

Если это мобильное или SPA-приложение:

  • Сворачивание/разворачивание:
    • сохраняется ли введенное значение и результат;
    • нет ли краша или сброса состояния без причины.
  • Рестарт приложения:
    • если требования предполагают:
      • сохранять ли последнее введенное значение?
      • стартовать ли с чистого экрана?
  • Поворот экрана (мобайл):
    • при смене ориентации:
      • не теряются ли данные;
      • корректно ли масштабируется интерфейс.
  1. Ошибки сети и взаимодействие с backend

Если логика проверки завязана на сервер (см. Вопрос 21):

Проверить:

  • Нет сети:
    • мгновенное понятное сообщение, отсутствие вечных лоадеров.
  • Медленная сеть:
    • ограниченный таймаут;
    • информирование о проблеме;
    • отсутствие “подвешенного” состояния.
  • Ошибки сервера:
    • 4xx/5xx:
      • различаем «некорректный ввод» и «проблема сервера»;
      • показываем корректные сообщения.
  • Повторные запросы:
    • нет дублирующих запросов при спаме кнопкой или нестабильной сети;
    • отсутствует неконтролируемый retry.

Пример упрощенного backend-контракта (Go):

// GET /is-number?value=...
// Ответ: { "is_number": true/false }

Тесты:

  • Проверить согласованность:
    • фронт показывает результат, соответствующий JSON-ответу;
    • при ошибках сети/сервера фронт не подменяет это на «не число».
  1. Безопасность

Даже простая форма — точка входа.

Проверить:

  • Обработка вредоносного ввода:
    • строки вида:
      • "1 OR 1=1"
      • "' OR ''='"
      • ""
      • длинные последовательности символов.
  • Ожидания:
    • не выполняется SQL/JS;
    • backend возвращает контролируемый ответ;
    • UI отображает безопасное сообщение об ошибке;
    • данные экранируются/валидируются.
  • Никаких:
    • stack trace-ов в ответах;
    • утечек деталей инфраструктуры;
    • ошибок 500 без обработки.

SQL-пример негативного теста:

-- Нежелательно видеть в логах/запросах что-то вроде:
SELECT * FROM numbers WHERE value = '' OR ''='';

Если подобное возможно — баг в серверной части.

  1. Производительность и устойчивость

Даже для простой операции важно убедиться в отсутствии деградаций.

Проверить:

  • Большое количество запросов за короткий период:
    • приложение не падает;
    • сервер не уходит в 500 от простого спама;
  • Очень длинный ввод (до максимально допустимой длины и за её пределами):
    • UI не зависает;
    • backend:
      • отфильтровывает/обрезает;
      • не падает по памяти или времени.
  1. Кросс-браузерность / кросс-платформенность

Если это веб:

  • Проверить:
    • разные браузеры (Chrome, Firefox, Safari, Edge);
    • десктоп/мобильные браузеры;
    • различия в HTML5-вводе (type="number" vs type="text" + JS-валидация).

Если это мобильное нативное:

  • Проверить:
    • разные версии ОС;
    • разные устройства и DPI;
    • поведение экранной клавиатуры:
      • показывается ли цифровая клавиатура, если ожидается число.
  1. Логи и наблюдаемость

Для зрелой системы:

  • Проверить, что:
    • чувствительные данные не логируются в открытом виде;
    • ошибки валидации логируются корректно (на уровне события, а не stack trace на каждый чих);
    • при необходимости есть минимальная телеметрия, по которой можно понять:
      • долю ошибочных вводов;
      • возможные злоупотребления.

Пример: аккуратный лог на Go:

log.Printf("is-number request: value_length=%d is_number=%v", len(input), isNumber)

Без вывода полного пользовательского ввода.

  1. Итоговая формулировка для интервью

Краткий сильный ответ:

  • Помимо проверки самой логики “число / не число”, я проверю:
    • UX: понятные сообщения, поведение при ошибках, доступность.
    • Разные форматы ввода: пробелы, знаки, дробные, локали, спецсимволы.
    • Состояния приложения: сворачивание, перезапуск, поворот, сохранность данных.
    • Сетевые сценарии (если есть backend): нет сети, таймауты, 4xx/5xx — без подвисаний и с корректными сообщениями.
    • Безопасность: устойчивость к мусорному вводу и инъекциям, отсутствие утечек и крэшей.
    • Кросс-браузерность / кросс-платформенность и поведение клавиатуры/элементов ввода.
    • Логирование и отсутствие лишней чувствительной информации в логах.

Такой подход демонстрирует взгляд на приложение как на продукт целиком, а не на одну условную функцию.

Вопрос 23. Есть ли планы по внедрению новых технологий или инструментов для упрощения тестирования в команде?

Таймкод: 00:57:38

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

Правильный ответ:

На такой вопрос важно показать понимание, что «внедрение новых технологий» — не самоцель. Главный фокус — системное повышение эффективности и надежности процесса тестирования: сокращение ручной рутины, ускорение обратной связи, повышение стабильности пайплайна и прозрачности качества.

Хороший ответ строится так:

  1. Осознанный подход к новым инструментам
  • Не гнаться за хайпом.
  • Оценивать:
    • ROI (что реально сократит трудозатраты и риски);
    • интегрируемость с текущим стеком (CI/CD, трекер, репозитории, мониторинг);
    • поддерживаемость командой (есть ли компетенции).
  • «Новые технологии» часто = эволюция существующих решений:
    • укрупнение сценариев автотестов;
    • повышение стабильности и наблюдаемости;
    • автоматизация операционных действий.
  1. Усиление существующей автоматизации

Если уже есть базовая инфраструктура (CI, автотесты, уведомляющие боты), логичный вектор:

  • Расширять покрытие автоматизацией:
    • критичные бизнес-флоу (регресс/смоук);
    • контрактные тесты для API;
    • интеграционные тесты между сервисами;
    • проверку миграций БД.
  • Повышать стабильность:
    • детерминированные тестовые данные;
    • изоляция окружений;
    • борьба с flaky-тестами (ретраи с метрикой, quarantine, анализ причин).

Пример (Go): простой smoke-тест API, запускаемый в CI:

func TestHealthcheck(t *testing.T) {
resp, err := http.Get(os.Getenv("SERVICE_URL") + "/health")
if err != nil {
t.Fatalf("healthcheck request failed: %v", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
t.Fatalf("unexpected status: %d", resp.StatusCode)
}
}
  1. Интеграция с инфраструктурой и ботами

Правильное направление — не только автотесты, но и «автоматизация вокруг»:

  • Боты-уведомители:
    • отправка результатов прогонов в Slack/Telegram/MS Teams:
      • статус пайплайна;
      • список свалившихся тестов с ссылками на логи/репорты;
      • триггеры для ответственных команд.
  • Автоматическое создание задач:
    • при падении регрессии по конкретному модулю:
      • создается задача с:
        • логами,
        • ссылкой на Allure/ReportPortal,
        • ответственным по сервису.
  • Автоматизация рутинных операций:
    • генерация тестовых данных;
    • ресет окружений;
    • запуск локальных стендов;
    • вспомогательные CLI/скрипты для тестировщиков и разработчиков.

Пример (Go): автосоздание задачи в трекере при падении теста (упрощенно, концептуально):

type Issue struct {
Title string `json:"title"`
Description string `json:"description"`
Assignee string `json:"assignee"`
}

func createIssueOnFailure(testName, logsURL string) error {
issue := Issue{
Title: "[AUTO] Failed test: " + testName,
Description: "Test failed. Logs: " + logsURL,
Assignee: "team-backend",
}

body, _ := json.Marshal(issue)
req, _ := http.NewRequest("POST", "https://tracker.local/api/issues", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusCreated {
return fmt.Errorf("issue creation failed with status %d", resp.StatusCode)
}
return nil
}
  1. Направления, которые логично развивать

Даже без «кардинальных революций» можно планировать:

  • Контрактные тесты и API-first:
    • валидация схем (OpenAPI/Protobuf);
    • гарантии совместимости между сервисами.
  • Observability-ориентированное тестирование:
    • использование логов, метрик, трассировок;
    • проверки SLO/SLA в рамках тестов.
  • Аналитика и качество тестов:
    • анализ flaky-тестов;
    • покрытие критичных маршрутов;
    • интеграция с Code Review (pre-merge проверки).

SQL-пример: мониторинг частоты падений тестов по историям прогонов:

SELECT test_name,
COUNT(*) AS failures,
COUNT(*) FILTER (WHERE status = 'failed')::float
/ COUNT(*) * 100 AS fail_rate_percent
FROM test_runs
GROUP BY test_name
HAVING COUNT(*) > 20
ORDER BY fail_rate_percent DESC;

Используя такие отчеты, команда может целенаправленно стабилизировать самые проблемные тесты.

  1. Как это озвучить на интервью

Сильный ответ:

  • Поддерживаю идею, что не всегда нужны резкие технологические повороты.
  • Фокус — на:
    • развитии текущей автоматизации;
    • лучшей интеграции с CI/CD;
    • ботах и сервисах, которые снижают ручную нагрузку и ускоряют фидбек.
  • При этом:
    • открыт к использованию новых инструментов там, где они реально дают выигрыш:
      • генерация тестовых данных;
      • контрактное тестирование;
      • улучшенная отчетность;
      • observability и quality gate’ы.

Такой ответ показывает зрелое, прагматичное отношение к инженерным практикам: не «давайте прикрутим модный фреймворк», а «давайте системно сделаем наш pipeline быстрее, прозрачнее и надежнее».

Вопрос 24. Как часто происходит ротация ролей и направлений внутри команды тестирования и кого она затрагивает?

Таймкод: 01:01:00

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

Правильный ответ:
При обсуждении ротации важно показать понимание баланса между:

  • стабильностью экспертизы по домену и компонентам,
  • диверсификацией опыта,
  • снижением bus factor,
  • адаптацией новых сотрудников без потери качества.

Ключевые аспекты грамотной ротации:

  1. Цели ротации:
  • Снижение рисков bus factor:
    • критичные области продукта (платежи, инвестиции, безопасность, push-уведомления, диплинки, аналитика) не должны быть завязаны на одного человека.
  • Рост кросс-функциональной экспертизы:
    • участники команды понимают архитектуру продукта целиком:
      • API, мобайл, веб, интеграции, инфраструктура.
  • Более справедливое распределение задач:
    • рутинные или «грязные» задачи (регресс, смоук, ручные проверки, triage) равномерно распределяются;
    • интересные задачи (R&D, автотесты, системный анализ) не концентрируются в одних руках.
  • Возможность профессионального развития:
    • переход между направлениями: мобильное тестирование, backend/API, нагрузка, security, аналитика.
  1. Операционная ротация внутри команды (еженедельная/регулярная):

Хорошая практика — легкая ротация ролей внутри одной команды:

  • Примеры ролевых зон:
    • ответственный за:
      • смоук/регресс релиза;
      • проверку конкретных фич;
      • triage инцидентов;
      • документацию тест-кейсов;
      • автотесты и инфраструктуру.
  • Еженедельная/спринтовая ротация:
    • помогает:
      • всем понимать жизненный цикл задач;
      • не выгорать на одном типе активности;
      • формировать взаимозаменяемость.
  • Важно:
    • ротация не должна ломать контекст:
      • крупные сложные фичи нужно доводить до конца теми же людьми или с контролируемой передачей знаний.
  1. Межкомандная ротация (раз в несколько месяцев):

Речь о переходах между доменами/продуктами:

  • Например:
    • из команды мобильного банкинга → в команду инвестиционного модуля;
    • из frontend QA → в API/интеграционное направление;
    • из функционального тестирования → в автоматизацию.
  • Разумная частота:
    • каждые 6–12 месяцев или по запросу человека и потребностям продукта.
  • Условия:
    • не в разгаре критичных релизов;
    • с планом передачи знаний;
    • с участием лида/менеджера, чтобы не проседало качество.
  1. Работа с новичками:

Ротация не должна «ломать» онбординг:

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

Чтобы ротация не снижала качество, нужны опоры:

  • Документация:
    • спецификации,
    • тест-кейсы,
    • чек-листы регресса,
    • схемы архитектуры.
  • Автоматизация:
    • стабильные автотесты по критичным сценариям;
    • они служат «страховкой», когда люди меняются.
  • Ответственные за домены:
    • даже при ротациях есть люди, глубже знающие конкретные области:
      • они помогают ревьюить тест-подход и результаты.
  1. Как правильно ответить на интервью:

Сильная позиция:

  • Ротация полезна, когда:
    • управляемая;
    • прозрачная;
    • поддержана документацией и автотестами;
    • учитывает уровень и фазу развития специалиста.
  • Еженедельная ротация задач внутри команды:
    • ок, если речь про виды активности (triage, регресс, автотесты), а не постоянную смену домена.
  • Межкомандная ротация раз в полгода+:
    • помогает развивать экспертизу и уменьшать риски.
  • Важно:
    • чтобы при ротации не терялось чувство ownership и ответственности за качество;
    • чтобы решения принимались осознанно, а не как хаотичная «чехарда ролей».

Такой ответ демонстрирует понимание влияния ротации на качество продукта, знания команды и устойчивость процессов, а не только «факт, что можно переходить между командами».

Вопрос 25. Как компания реагирует на критические инциденты: привлекают ли сотрудников в нерабочее время и насколько стабилен функционал?

Таймкод: 01:02:50

Ответ собеседника: правильный. Уточнил про вызов сотрудников ночью или из отпуска и компенсацию. Получил ответ, что таких ситуаций недавно не было, функционал стабилен и ночных экстренных работ не требовалось.

Правильный ответ:

При обсуждении реакции на критические инциденты важно показать понимание:

  • как должна выглядеть зрелая система incident management;
  • как связаны стабильность функционала, качество тестирования и необходимость ночных эскалаций;
  • чем отличается нормальная эксплуатация от «постоянного героизма».

Ключевые элементы грамотного процесса:

  1. Профилактика вместо постоянных «пожаров»

Зрелая инженерная культура строится так, чтобы:

  • минимизировать вероятность критических инцидентов за счет:
    • строгих quality gate в CI/CD (юнит-тесты, интеграционные, e2e, статический анализ, линтеры);
    • продуманных регрессионных наборов (автоматизированных и ручных) для критичных сценариев;
    • канареечных релизов, blue-green, feature-флагов;
    • продуманной схемы миграций БД;
    • мониторинга и алертинга (latency, error rate, бизнес-метрики).
  • в итоге:
    • вызовы «поднимите кого-то ночью» становятся редким исключением, а не нормой.

Пример: проверка критичного сервиса перед релизом (Go):

func TestCriticalFlow(t *testing.T) {
// smoke / sanity для ключевого бизнес-флоу
// если это падает в CI — релиз блочится
}
  1. Формализованный процесс реагирования на инциденты

Даже если инциденты редки, для критичных систем (финансы, инвестиции, платежи) должен быть понятный процесс:

  • SLA/приоритеты:
    • P0/P1 — полная недоступность сервиса, критичные деньги/операции, утечка данных;
    • P2/P3 — частичные деградации, некритичные функции.
  • Для P0/P1:
    • обычно:
      • есть on-call (дежурные по графику);
      • возможен вызов в нерабочее время;
      • это:
        • компенсируется (деньгами или отгулами),
        • регламентировано, а не «по дружбе».
  • Для менее критичных:
    • инциденты обрабатываются в рабочее время:
      • без дергания людей ночью.

Если в реальности за длительный период не было ночных эскалаций — это хороший сигнал о:

  • стабильности функционала;
  • адекватной релизной политике;
  • эффективном тестировании до продакшена.
  1. Роль тестирования и автотестов в снижении количества инцидентов

Важно связать стабильность продакшена с техническими практиками, а не только «нам повезло».

Обычно это:

  • автоматизированные smoke и регресс-запуски перед релизами;
  • постоянные nightly и pre-release прогоны;
  • проверка контрактов между сервисами;
  • тестирование сценариев деградации:
    • падение зависимостей,
    • таймауты,
    • ошибки БД.

SQL-пример бизнес-мониторинга (косвенный контроль инцидентов):

SELECT
DATE_TRUNC('hour', created_at) AS ts,
COUNT(*) FILTER (WHERE status = 'success') AS ok_count,
COUNT(*) FILTER (WHERE status = 'failed') AS fail_count
FROM payments
WHERE created_at >= NOW() - INTERVAL '24 hours'
GROUP BY ts
ORDER BY ts;

Резкие аномалии (рост fail_count) могут триггерить алерт еще до массовых жалоб пользователей.

  1. Как отвечать на вопрос на интервью

Сильная позиция:

  • Понимать, что:
    • редкость ночных вызовов и отсутствие постоянных экстренных фиксов — признак зрелых процессов и качественного тестирования.
  • При этом:
    • признавать, что для реально критичных инцидентов должен существовать формальный on-call/incident-процесс:
      • понятные критерии, когда будить людей;
      • понятная компенсация;
      • обязательный postmortem:
        • анализ причин,
        • исправление,
        • улучшение тестов, мониторинга, логирования.

Формулировка:

  • Корректно, когда:
    • компания не живет в режиме перманентного 24/7 героизма;
    • критические инциденты редки за счет качественной инженерии;
    • но при этом есть готовая процедура быстрой реакции и компенсации, если действительно происходит что-то, что затрагивает деньги, безопасность или данные пользователей.

Такой ответ демонстрирует понимание связи между качеством системы, культурой on-call, тестированием и реальной эксплуатацией продукта.

Вопрос 26. Есть ли у тебя текущие офферы и какие предпочтения по типу работы и развитию?

Таймкод: 01:04:28

Ответ собеседника: правильный. Сообщает о наличии одного оффера с дедлайном и возможном втором; отмечает, что ключевой приоритет — развитие в автоматизации, а не выбор между вебом и мобилкой, с поэтапным переходом от смешанного формата (ручное + автотесты) к увеличению доли автоматизации.

Правильный ответ:

На такой вопрос важно ответить честно и одновременно показать зрелость в планировании карьеры и осознанный выбор стека/формата работы.

Ключевые моменты сильного ответа:

  • Прозрачность по текущим обязательствам:

    • Если есть оффер с дедлайном:
      • честно обозначить сроки, чтобы компания могла спланировать процесс:
        • "У меня есть предложение с дедлайном до [дата]; готов оперативно проходить ваши этапы и предоставить ответ."
    • Если есть другие процессы:
      • кратко упомянуть, без лишних деталей:
        • это нормально и воспринимается как стандартная практика рынка.
  • Приоритеты по типу работы:

    • Фокус не на «веб vs мобайл» как самоцели, а на:
      • технологиях,
      • качестве процессов,
      • возможностях роста.
    • Зрелая формулировка:
      • "Мне важно работать там, где:
        • есть осмысленная автоматизация,
        • есть возможность влиять на качество архитектурно,
        • есть живой CI/CD, нормальные пайплайны,
        • есть культура инженерного подхода к тестированию."
  • Развитие в автоматизации:

    • Осознанный путь:
      • старт в роли, где:
        • можно сочетать ручное тестирование сложных сценариев,
        • и постепенно брать ответственность за автотесты.
      • по мере погружения:
        • увеличивать долю автотестов:
          • API-тесты (например, на Go),
          • UI-тесты,
          • контрактные тесты;
        • участвовать в построении тестовой архитектуры:
          • структура репозиториев,
          • интеграция с CI/CD,
          • тестовые данные, mock-и, контейнеризированные стенды.
    • Важно подчеркнуть:
      • "Я не хочу просто нажимать кнопки; мне важно строить воспроизводимый, автоматизированный процесс проверки качества."
  • Гибкость по домену и платформе:

    • Хороший сигнал — готовность работать:
      • с вебом, мобильными клиентами, backend/API, микросервисами;
      • при условии, что это даёт возможность развивать инженерные навыки.
    • Правильный акцент:
      • "Готов подключаться к любому из направлений (web/mobile/backend-тестирование), если там есть здоровая техническая среда и возможность масштабировать автоматизацию."
  • Что такой ответ транслирует компании:

    • человек:
      • открыт и честен по срокам и офферам;
      • выбирает не «по случайности», а по качеству инженерной культуры;
      • нацелен развиваться в сторону более технической роли:
        • написание автотестов,
        • понимание CI/CD,
        • работа с кодом, логами, API,
        • а не оставаться только в ручном регрессе.

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