QA Automation Engineer: Автоматизация тестирования на Kotlin для Финтеха

Практический курс по автоматизации тестирования веб, мобильных приложений и API на Kotlin с фокусом на финтех-проекты. Вы освоите полный стек технологий от основ SDLC и работы с Git/Bash до построения CI/CD пайплайнов, контейнеризации в Docker и создания собственного тестового фреймворка.

1. Жизненный цикл ПО и теория тестирования

Жизненный цикл ПО и теория тестирования

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

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

Жизненный цикл разработки ПО (SDLC)

Жизненный цикл разработки программного обеспечения (Software Development Life Cycle, SDLC) — это последовательность этапов, которые проходит программный продукт от момента появления идеи до снятия с эксплуатации. SDLC обеспечивает предсказуемость, управляемость и прозрачность процесса создания ПО.

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

1. Сбор и анализ требований

На этом этапе бизнес-аналитики и менеджеры продуктов общаются с заказчиками, чтобы понять, что именно нужно создать. Формируются бизнес-требования и технические спецификации.

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

2. Проектирование (Design)

Архитекторы и разработчики решают, как технически реализовать собранные требования. Выбираются языки программирования (например, Java или Kotlin), базы данных (PostgreSQL, Oracle), архитектурные паттерны (микросервисы или монолит) и протоколы взаимодействия (REST API, gRPC).

3. Разработка (Implementation/Coding)

Программисты пишут исходный код продукта согласно проектной документации. Базы данных настраиваются, API-интерфейсы программируются, создается пользовательский интерфейс (UI).

4. Тестирование (Testing)

Готовый код проверяется на соответствие изначальным требованиям. Именно здесь активно работают QA-инженеры. Выявляются дефекты, код возвращается на доработку, и цикл повторяется до достижения приемлемого уровня качества.

5. Внедрение (Deployment)

Проверенный продукт переносится на «боевые» серверы (Production), где становится доступен реальным пользователям.

6. Поддержка (Maintenance)

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

> Качество закладывается на этапе требований, а не на этапе тестирования. Если требование изначально ошибочно, разработчик напишет идеальный код, который будет делать абсолютно неправильные вещи. > > Лаборатория качества

Модели SDLC: Waterfall против Agile

Исторически первой моделью SDLC был Каскадный подход (Waterfall). В нем каждая следующая фаза начинается строго после завершения предыдущей. Вернуться на этап проектирования, если вы уже находитесь на этапе тестирования, практически невозможно без огромных финансовых потерь.

Сегодня в финтехе доминирует Agile (гибкие методологии, такие как Scrum или Kanban). Процесс разбивается на короткие итерации (спринты) длиной 2–4 недели. В конце каждого спринта команда выпускает рабочий кусочек продукта.

| Характеристика | Waterfall | Agile (Scrum) | | :--- | :--- | :--- | | Отношение к изменениям | Изменения вносить сложно и дорого | Изменения приветствуются на любом этапе | | Роль тестирования | Строго в конце разработки | Непрерывно, параллельно с разработкой | | Документация | Исчерпывающая, создается до написания кода | Минимально необходимая, обновляется на лету | | Риски для финтеха | Высокий риск выпустить устаревший продукт | Риск технического долга из-за спешки |

Жизненный цикл тестирования ПО (STLC)

Тестирование — это не просто хаотичное «прокликивание» кнопок в приложении. Это самостоятельный инженерный процесс, который имеет свой собственный жизненный цикл — Жизненный цикл тестирования ПО (Software Testing Life Cycle, STLC).

STLC тесно переплетается с SDLC. В современных Agile-командах они идут параллельно.

!Схема взаимодействия SDLC и STLC

Рассмотрим этапы STLC на практическом примере: команда разрабатывает API для расчета комиссии при переводе с кредитной карты на дебетовую.

1. Анализ требований (Requirement Analysis)

QA-инженер изучает документацию еще до того, как написана первая строчка кода. Цель — проверить сами требования на полноту, непротиворечивость и тестируемость.

Пример из практики: в документации указано, что комиссия за перевод составляет 2%. QA-инженер задает вопросы: «А есть ли минимальная сумма комиссии? Что делать, если 2% составляют дробное число копеек — как мы округляем: математически или всегда в большую сторону?». Найдя эти пробелы до начала разработки, тестировщик экономит компании десятки часов работы программистов.

2. Планирование тестирования (Test Planning)

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

Для нашего API комиссии мы решаем: функциональное тестирование будет автоматизировано с использованием Kotlin и библиотеки REST Assured. Нагрузочное тестирование не требуется, так как ожидаемая нагрузка низкая.

3. Проектирование тестов (Test Design)

QA-инженер создает тест-кейсы (Test Cases) — пошаговые сценарии проверок. Применяются техники тест-дизайна (классы эквивалентности, граничные значения), чтобы минимизировать количество тестов, сохранив максимальное покрытие.

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

  • Перевод 100 руб. (проверка минимального порога).
  • Перевод 10 000 руб. (стандартный расчет).
  • Перевод 0 руб. (ожидаем ошибку валидации).
  • Перевод -50 руб. (ожидаем ошибку валидации).
  • 4. Настройка тестовой среды (Environment Setup)

    Тестировать на «боевой» базе данных с реальными деньгами клиентов категорически запрещено. Инженеры разворачивают тестовые стенды — изолированные копии системы. Настраиваются базы данных с фейковыми пользователями, поднимаются моки (заглушки) для сторонних сервисов (например, заглушка платежного шлюза Visa/Mastercard).

    5. Выполнение тестов (Test Execution)

    Разработчики отдают готовый код. QA-инженер запускает написанные автотесты или проходит тест-кейсы вручную. Фактический результат работы программы сравнивается с ожидаемым.

    Если при переводе 10 000 руб. система списывает комиссию 200 руб. (как ожидалось) — тест пройден. Если списывается 2000 руб. — тест провален, заводится дефект.

    6. Завершение тестирования (Test Closure)

    Анализ результатов. Формируется отчет о качестве продукта (например, с помощью инструмента Allure). Команда принимает решение: можно ли выпускать продукт в релиз. Если критических багов нет — даем зеленый свет.

    Фундаментальные принципы тестирования

    Международный совет по тестированию ПО (ISTQB) выделяет 7 базовых принципов, которые должен знать каждый инженер.

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

    2. Исчерпывающее тестирование невозможно. Проверить все возможные комбинации входных данных нереально. Представьте поле ввода PIN-кода из 4 цифр. Это 10 000 комбинаций. А если полей 10? Вместо попыток проверить всё, QA-инженеры используют анализ рисков и техники тест-дизайна для выбора наиболее важных проверок.

    3. Раннее тестирование экономит время и деньги. Этот принцип лежит в основе подхода Shift-Left Testing (сдвиг влево). Чем раньше найден баг, тем дешевле его исправить. Ошибка в требованиях, найденная на этапе анализа, стоит 0 рублей (нужно просто переписать текст). Та же ошибка, найденная в Production, может стоить миллионы.

    4. Кластеризация дефектов. Работает принцип Парето (80/20): около 80% всех багов обычно сосредоточено в 20% модулей программы. В финтехе самыми «забагованными» часто оказываются модули интеграции с устаревшими банковскими системами (Legacy) или сложные калькуляторы процентов.

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

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

    7. Заблуждение об отсутствии ошибок. Создание системы, в которой нет багов, бесполезно, если эта система не отвечает потребностям бизнеса. Идеально работающий, но никому не нужный продукт — это провал.

    QA, QC и Тестирование: в чем разница?

    В индустрии часто путают эти три термина, используя их как синонимы. Однако это вложенные друг в друга понятия.

    Обеспечение качества (Quality Assurance, QA) — это превентивный процесс. QA сфокусирован на улучшении процессов разработки, чтобы баги вообще не появлялись. QA-инженер анализирует требования, настраивает CI/CD пайплайны, внедряет стандарты написания кода.

    Контроль качества (Quality Control, QC) — это реактивный процесс. Продукт уже написан, и задача QC — проверить его на соответствие требованиям.

    Тестирование (Testing) — это технический процесс в рамках QC. Это непосредственно написание автотестов на Kotlin, выполнение SQL-запросов к базе данных для проверки целостности данных, отправка HTTP-запросов через Postman.

    Жизненный цикл дефекта (Bug Lifecycle)

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

    Управление багами происходит в системах трекинга задач (например, Jira). Жизненный цикл бага состоит из нескольких статусов:

  • New (Новый): Тестировщик завел баг-репорт. Описал шаги для воспроизведения, приложил логи и скриншоты.
  • Assigned (Назначен): Менеджер или тимлид посмотрел баг и назначил его на конкретного разработчика.
  • In Progress (В работе): Разработчик начал искать причину ошибки и писать код для ее исправления.
  • Fixed (Исправлен): Разработчик внес изменения в код и залил их в репозиторий (Git).
  • Ready for Test (Готов к тестированию): Исправленный код развернут на тестовом стенде.
  • Verified (Проверен): QA-инженер заново выполняет тест. Если ошибка исчезла, баг переводится в статус Closed (Закрыт).
  • Reopened (Переоткрыт): Если QA-инженер видит, что ошибка все еще воспроизводится, баг возвращается разработчику.
  • Анатомия идеального баг-репорта

    Чтобы разработчик мог быстро исправить баг, QA Automation Engineer должен уметь грамотно его описать. Плохой баг-репорт: «Кнопка перевода не работает». Хороший баг-репорт содержит:

  • Summary (Краткое описание): Что, где и при каких условиях произошло. Пример: Ошибка 500 при попытке перевода отрицательной суммы (-100) через REST API /v1/transfer.
  • Environment (Окружение): Версия приложения, тестовый стенд, ОС.
  • Steps to reproduce (Шаги для воспроизведения): Точная последовательность действий. В случае API — тело HTTP-запроса.
  • Expected result (Ожидаемый результат): Система должна вернуть HTTP статус 400 Bad Request и сообщение "Сумма не может быть отрицательной".
  • Actual result (Фактический результат): Система возвращает HTTP статус 500 Internal Server Error и падает с NullPointerException.
  • Экономика автоматизации тестирования

    Зачем финтех-компании нанимают QA Automation инженеров и платят им высокие зарплаты? Ответ кроется в математике и скорости доставки продукта на рынок (Time-to-Market).

    Ручное регрессионное тестирование (проверка того, что новый код не сломал старый функционал) крупного банковского приложения может занимать у команды из 5 человек целую неделю. В Agile релизы нужно делать каждые 2 недели. Тратить половину времени спринта на ручные проверки — непозволительная роскошь.

    Автотесты, написанные на Java или Kotlin, могут выполнить ту же работу за 15 минут.

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

    Где:

  • — финансовая выгода (сэкономленные часы ручных тестировщиков, предотвращенные критические баги в Production).
  • — затраты на автоматизацию (зарплата автоматизатора, настройка серверов, поддержка инфраструктуры).
  • Автоматизация требует высоких стартовых инвестиций (), но на длинной дистанции, когда количество регрессионных прогонов исчисляется сотнями, многократно превышает затраты.

    Однако автоматизировать всё подряд — антипаттерн. Не подлежат автоматизации:

  • Требования, которые постоянно меняются (вы будете переписывать тесты каждый день).
  • Одноразовые фичи (промо-акции на 2 дня).
  • UX/UI с точки зрения удобства пользователя (автотест не поймет, что зеленая кнопка сливается с зеленым фоном, если в коде цвета указаны верно).
  • Подготовка к практике

    Теория тестирования и понимание SDLC/STLC — это компас, который не даст вам потеряться в мире кода. В следующих статьях мы перейдем к практической реализации этих концепций. Мы разберем архитектуру клиент-серверных приложений, погрузимся в протокол HTTP, научимся работать с командной строкой Bash и системой контроля версий Git, чтобы подготовить инфраструктуру для написания наших первых автотестов на Kotlin.

    10. Основы автоматизации веб-приложений

    Основы автоматизации веб-приложений

    На предыдущих этапах мы научились проверять бизнес-логику финтех-продуктов через API, отправляя JSON-запросы напрямую к серверу. API-тесты работают молниеносно и отличаются высокой стабильностью. Однако конечные пользователи — клиенты банка — не отправляют HTTP-запросы через командную строку. Они взаимодействуют с графическим интерфейсом: нажимают кнопки, заполняют формы и читают всплывающие уведомления.

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

    Анатомия веб-приложения: DOM и элементы

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

  • HTML (HyperText Markup Language) — задает структуру страницы (где находится текст, где картинка, где кнопка).
  • CSS (Cascading Style Sheets) — отвечает за внешний вид (цвета, шрифты, отступы).
  • JavaScript (JS) — обеспечивает интерактивность (анимации, отправка данных без перезагрузки страницы, валидация полей).
  • Когда браузер загружает HTML-код, он преобразует его в древовидную структуру, которая называется DOM (Document Object Model — Объектная модель документа).

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

    Представьте себе строительство банковского отделения. HTML — это кирпичные стены и планировка комнат. CSS — это краска на стенах, корпоративные цвета и мебель. JavaScript — это автоматические двери, камеры наблюдения и система электронной очереди. DOM — это подробный архитектурный план здания, в котором указаны точные координаты каждого объекта.

    Для автотеста веб-страница — это не картинка, а именно DOM-дерево. Скрипт не «видит» кнопку глазами, он ищет ее узел в структуре DOM.

    !Интерактивный визуализатор CSS-селекторов

    Локаторы: Искусство поиска элементов

    Чтобы кликнуть по кнопке или ввести текст в поле, автотест должен однозначно идентифицировать нужный элемент в DOM-дереве. Инструкция, описывающая путь к элементу, называется локатором.

    Существует два основных языка запросов для поиска элементов: CSS-селекторы и XPath.

    CSS-селекторы

    Это нативный для браузера способ поиска элементов, который используется самими разработчиками для применения стилей. CSS-селекторы работают очень быстро и отличаются лаконичностью.

    * Поиск по тегу: button (найдет все кнопки на странице) * Поиск по классу (ставится точка): .login-btn * Поиск по идентификатору (ставится решетка): #user-email * Поиск по атрибуту: input[name='password']

    XPath (XML Path Language)

    Более мощный, но медленный и громоздкий язык запросов. В отличие от CSS, XPath позволяет искать элементы по их текстовому содержимому и двигаться по DOM-дереву не только вниз (к дочерним элементам), но и вверх (к родительским).

    * Поиск по атрибуту: //input[@name='password'] * Поиск по тексту: //button[text()='Войти'] * Поиск родителя: //div[@class='error-message']/parent::div

    Стратегия стабильных локаторов в Финтехе

    Главная проблема UI-автотестов — их хрупкость. Разработчик может изменить цвет кнопки (изменится класс) или перенести ее в другой блок (изменится структура XPath), и тест упадет, хотя функциональность не сломалась.

    > Надежный локатор не должен зависеть от дизайна или структуры верстки. Идеальный подход — использование специальных тестовых атрибутов, таких как data-testid. > > Первый шаг в автоматизации тестирования веб-приложений / Хабр

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

  • Плохо (завязка на верстку): //div/div[2]/form/input[1] — сломается при добавлении любого нового элемента на форму.
  • Удовлетворительно (завязка на стили): .amount-input-field — сломается, если дизайнер решит переименовать CSS-классы при редизайне.
  • Отлично (тестовый атрибут): [data-testid='transfer-amount-input'] — этот атрибут добавляется разработчиками специально для QA-инженеров и никогда не меняется при редизайне.
  • Архитектура взаимодействия: Как код управляет браузером

    Код на Kotlin не может напрямую нажимать кнопки в Google Chrome. Для этого нужен посредник. Исторически индустриальным стандартом является Selenium WebDriver — протокол, который переводит команды из кода в инструкции, понятные конкретному браузеру.

    Однако писать тесты на голом Selenium сложно: он требует ручного управления ожиданиями и сложной настройки. Поэтому в экосистеме Kotlin/Java используются высокоуровневые фреймворки-обертки, такие как Selenide или современные альтернативы вроде Playwright.

    !Архитектура автоматизации веб-тестирования

    В нашем курсе мы сфокусируемся на Selenide, так как он решает главную боль UI-тестирования — проблему синхронизации.

    Проблема синхронизации и Умные ожидания

    Веб-приложения асинхронны. Когда вы нажимаете кнопку «Оплатить», браузер отправляет запрос на сервер. Ответ может прийти через 100 миллисекунд, а может через 3 секунды. Если автотест попытается проверить сообщение об успехе сразу после клика, он упадет с ошибкой ElementNotFound (Элемент не найден), потому что сообщение еще не появилось в DOM.

    Начинающие инженеры часто совершают фатальную ошибку, используя жесткие паузы:

    Это приводит к двум проблемам:

  • Если сервер ответит за 1 секунду, тест будет впустую ждать еще 4 секунды. При 1000 тестов это добавит часы к времени прогона.
  • Если сервер из-за нагрузки ответит за 6 секунд, тест упадет.
  • Selenide использует Умные (явные) ожидания. Когда мы просим Selenide найти элемент, он не падает сразу, если элемента нет. Он начинает опрашивать DOM-дерево каждые 100 миллисекунд на протяжении заданного таймаута (по умолчанию 4 секунды). Как только элемент появляется, тест мгновенно идет дальше.

    Написание первого UI-теста на Kotlin

    Подготовим инфраструктуру. В файле build.gradle.kts добавим зависимость Selenide к нашему тестовому движку JUnit 5:

    Напишем тест авторизации в интернет-банке. Мы будем использовать паттерн AAA (Arrange, Act, Assert).

    kotlin import com.codeborne.selenide.logevents.SelenideLogger import io.qameta.allure.selenide.AllureSelenide import org.junit.jupiter.api.BeforeAll

    class BaseWebTest { companion object { @BeforeAll @JvmStatic fun setupAllure() { // Включаем интеграцию Selenide и Allure SelenideLogger.addListener("AllureSelenide", AllureSelenide().screenshots(true).savePageSource(true)) } } } ``

    Теперь, выполнив команду ./gradlew allureReport`, мы получим отчет, где к каждому упавшему тесту прикреплен скриншот экрана и полный HTML-код страницы (Page Source) на момент падения. Это позволяет расследовать дефекты верстки, даже не перезапуская тест локально.

    Автоматизация веб-интерфейсов требует баланса. Из-за их хрупкости и медлительности, согласно Пирамиде тестирования, UI-тестов должно быть меньше, чем API-тестов. Они должны покрывать только критические пользовательские пути (End-to-End сценарии), оставляя проверку сложных математических расчетов и граничных значений на откуп API и Unit-тестам.

    11. Веб-тестирование с использованием Kotlin

    Веб-тестирование с использованием Kotlin

    Индустрия автоматизации тестирования переживает смену парадигм. Исторически стандартом де-факто для взаимодействия с браузером являлся протокол Selenium WebDriver. Однако современные веб-приложения в финтехе — это сложные одностраничные интерфейсы (SPA), которые непрерывно обмениваются данными с сервером через фоновые запросы. Для тестирования таких систем требуются инструменты нового поколения, обеспечивающие двунаправленную связь с браузером и высокую скорость выполнения.

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

    Эволюция архитектуры: от WebDriver к протоколу CDP

    Чтобы понять преимущества современных инструментов, необходимо рассмотреть, как код управляет браузером на фундаментальном уровне.

    Архитектура Selenium основана на HTTP-запросах. Когда автотест вызывает команду клика по кнопке, происходит следующий процесс:

  • Тестовый фреймворк формирует HTTP-запрос.
  • Запрос отправляется к промежуточному серверу (например, ChromeDriver).
  • ChromeDriver транслирует команду в инструкции, понятные браузеру.
  • Браузер выполняет действие и возвращает результат по той же цепочке.
  • Этот подход имеет критический недостаток — однонаправленность и высокие накладные расходы на сетевое взаимодействие. Тестовый фреймворк не знает, что происходит внутри браузера в реальном времени. Он вынужден постоянно отправлять запросы с вопросом: «Элемент уже появился? А теперь?», что приводит к нестабильности (flaky tests).

    Современные инструменты, такие как Playwright, используют протокол CDP (Chrome DevTools Protocol). Вместо отправки разовых HTTP-запросов, Playwright устанавливает постоянное WebSocket-соединение напрямую с ядром браузера.

    !Схема архитектуры Playwright в сравнении с Selenium WebDriver

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

    Иерархия изоляции: Browser, Context и Page

    Одной из главных проблем UI-тестирования является обеспечение независимости тестов. Если два теста запускаются параллельно в одном браузере, они могут перезаписать файлы cookie или данные локального хранилища друг друга (например, токены авторизации), что приведет к ложным падениям.

    В традиционном подходе для изоляции приходилось запускать новый экземпляр браузера для каждого теста. Запуск браузера — ресурсоемкая операция, занимающая несколько секунд.

    Playwright решает эту проблему через трехуровневую архитектуру:

  • Browser (Браузер) — физический процесс браузера (Chrome, Firefox, Safari). Запускается один раз за весь прогон тестов.
  • BrowserContext (Контекст браузера) — легковесный изолированный профиль внутри браузера. Имеет собственные cookie, кэш и локальное хранилище. Создание контекста занимает миллисекунды.
  • Page (Страница) — отдельная вкладка внутри контекста.
  • Для каждого нового теста создается новый BrowserContext. Это гарантирует абсолютную изоляцию данных при минимальных затратах времени.

    > Один язык позволяет покрыть все стеки: бэкенд — легко, mobile — синонимы со словом Kotlin, Web — как оказалось, тоже очень даже ничего. > > Автоматизируем тесты UI с помощью Kotlin / Хабр

    Математика параллельного выполнения

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

    Где — общее ускорение прогона тестов, — доля времени, которую занимают независимые тесты (поддающиеся распараллеливанию), а — количество параллельных потоков (контекстов браузера).

    Если прогон 100 UI-тестов последовательно занимает 20 минут, и 90% этого времени уходит на сами шаги тестов (), а 10% на нераспараллеливаемую настройку окружения, то при запуске в 4 потока () общее ускорение составит:

    Время выполнения сократится с 20 минут до примерно 6.5 минут. Использование BrowserContext позволяет увеличивать без экспоненциального роста потребления оперативной памяти сервера.

    Настройка инфраструктуры на Kotlin

    Для интеграции Playwright в проект на Kotlin с использованием JUnit 5, необходимо добавить зависимости в файл сборки build.gradle.kts:

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

    Продвинутый Page Object с функциями Kotlin

    В предыдущих статьях мы рассматривали паттерн Page Object Model (POM). Язык Kotlin позволяет сделать реализацию этого паттерна невероятно лаконичной благодаря Scope-функциям (apply, with, run) и свойствам (Properties).

    Вместо создания громоздких методов, возвращающих this для построения цепочек вызовов (Fluent Interface), в Kotlin достаточно использовать функцию apply.

    Рассмотрим страницу перевода средств в интернет-банке:

    Теперь сам автотест выглядит как читаемый бизнес-сценарий:

    Мокирование сетевых запросов (Network Interception)

    Самая мощная возможность современных UI-фреймворков — управление сетевым слоем прямо из браузера.

    В финтехе критически важно тестировать негативные сценарии. Как поведет себя веб-интерфейс, если процессинговый центр отклонит транзакцию из-за подозрений в мошенничестве (Anti-Fraud система)? Воспроизвести такую ситуацию на реальном тестовом стенде сложно: нужно искать специальную тестовую карту, которая триггерит блокировку, или просить разработчиков изменить настройки базы данных.

    С помощью Playwright мы можем перехватить HTTP-запрос, который фронтенд отправляет на бэкенд при нажатии кнопки «Перевести», и подменить ответ сервера (сделать Мок).

    !Интерактивная визуализация перехвата сетевого запроса

    Реализуем этот сценарий на Kotlin:

    В этом тесте реальный бэкенд даже не узнал о попытке перевода. Браузер отправил запрос, Playwright перехватил его на лету, мгновенно вернул ошибку 403, и мы проверили, как клиентский код (JavaScript/React) отрендерил эту ошибку на экране. Это яркий пример концепции Shift-Left Testing — мы тестируем обработку ошибок фронтендом изолированно и быстро.

    Интеграция UI-тестов с базами данных

    Несмотря на мощь мокирования, основные E2E-тесты (End-to-End) должны проходить систему насквозь, затрагивая реальную базу данных.

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

    Подготовка данных через UI (создание пользователя путем заполнения длинных форм регистрации) делает тесты невыносимо долгими. Правильный подход — использовать SQL-запросы для мгновенной подготовки состояния БД перед тестом.

    В экосистеме Kotlin для работы с БД часто используется библиотека JetBrains Exposed, которая позволяет писать типобезопасные SQL-запросы прямо на Kotlin.

    Пример интеграции БД и UI-теста:

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

    Инфраструктура: Bash, CI/CD и Allure Traces

    Автотесты приносят пользу только тогда, когда они запускаются автоматически при каждом изменении кода (в пайплайнах CI/CD). На удаленных Linux-серверах тесты запускаются через командную строку Bash.

    Команда для запуска конкретного класса тестов через Gradle:

    ./gradlew test --tests "IntegratedTransferTest"

    Когда UI-тест падает в CI/CD, инженеру сложно понять причину без визуального контекста. Обычного скриншота часто недостаточно — элемент мог мигнуть, или страница находилась в процессе анимации.

    Playwright предоставляет инструмент Tracing (Трассировка). Это полная запись жизни теста: DOM-дерево в каждую миллисекунду, сетевые запросы, логи консоли и движения мыши. Трассировка сохраняется в виде ZIP-архива.

    Для интеграции с Allure Framework достаточно добавить слушатель, который при падении теста будет прикреплять этот архив к отчету. Открыв Allure-отчет, QA-инженер может буквально «прокрутить время назад» и посмотреть, как выглядела страница за секунду до падения, какие запросы уходили на сервер и какие ответы возвращались. Это сокращает время расследования плавающих дефектов (flaky bugs) с часов до минут.

    12. Специфика мобильного тестирования

    Специфика мобильного тестирования

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

    Автоматизация мобильного тестирования кардинально отличается от веб-тестирования. Смартфон — это устройство, которое постоянно меняет свое состояние: теряет сеть в метро, принимает звонки, переключается между Wi-Fi и LTE, разряжается и меняет ориентацию экрана.

    Архитектура мобильных приложений

    Перед написанием автотестов необходимо понимать, с каким типом приложения предстоит работать. От этого зависит выбор инструментов и стратегии поиска элементов (локаторов).

  • Нативные приложения (Native Apps). Разрабатываются специально под конкретную платформу с использованием ее официальных языков и SDK (Swift/Objective-C для iOS, Kotlin/Java для Android). Они обладают максимальной производительностью, прямым доступом к аппаратному обеспечению (FaceID, NFC, камера) и высокой безопасностью. Большинство банковских приложений — нативные.
  • Веб-приложения (Mobile Web). Адаптивные версии сайтов, открываемые через мобильный браузер (Chrome, Safari). Тестируются теми же инструментами, что и десктопный веб (например, Playwright или Selenide), но с эмуляцией мобильного разрешения.
  • Гибридные приложения (Hybrid Apps). Представляют собой веб-приложение, «завернутое» в нативную оболочку. Основной контент отображается через встроенный браузерный компонент (WebView).
  • Кроссплатформенные приложения (Cross-platform). Написаны на едином фреймворке (Flutter, React Native) и компилируются под обе платформы.
  • | Характеристика | Нативные | Гибридные (WebView) | Кроссплатформенные | | :--- | :--- | :--- | :--- | | Производительность | Высокая | Низкая | Средняя/Высокая | | Доступ к железу | Полный | Ограниченный | Почти полный | | Сложность автоматизации| Средняя (нужны разные тесты для iOS и Android) | Высокая (переключение контекстов) | Средняя (единая кодовая база) |

    Уникальные вызовы мобильного тестирования

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

    Фрагментация устройств

    Экосистема Android насчитывает тысячи комбинаций устройств от разных производителей с модифицированными оболочками (MIUI, OneUI), разными разрешениями экранов и версиями операционных систем.

    Масштаб проблемы можно описать простой формулой комбинаторики:

    Где — общее количество уникальных тестовых конфигураций, — количество поддерживаемых версий ОС, — варианты разрешений экрана, а — количество производителей смартфонов. Если банк поддерживает 5 версий Android, 4 базовых разрешения и 3 основных производителей, то для полного покрытия одного сценария потребуется запусков теста.

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

    Прерывания (Interruptions)

    Мобильное устройство живет в агрессивной среде. Автотест должен проверять, как приложение обрабатывает внешние события. Что произойдет, если в момент подтверждения перевода по SMS пользователю поступит входящий телефонный звонок?

    Качественный автотест в финтехе должен уметь имитировать:

  • Входящие звонки и SMS.
  • Сворачивание приложения в фон (Background) и возврат на передний план (Foreground).
  • Появление системных диалогов (запрос разрешений на геолокацию или пуш-уведомления).
  • Нестабильность сети

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

    > Если клиент нажал кнопку «Оплатить», и в этот момент поезд заехал в туннель (сеть пропала), приложение не должно отправлять запрос повторно при появлении сети без ведома пользователя. Иначе произойдет двойное списание средств. > > kz.hexlet.io

    Автотесты должны переключать состояния сети (Airplane Mode, 3G, LTE) прямо во время выполнения шагов, проверяя корректность работы HTTP-клиента приложения и обработку статус-кодов (например, таймаутов).

    Эмуляторы против реальных устройств

    Для запуска мобильных автотестов нужна инфраструктура. Существует два основных подхода.

    Эмуляторы (Android) и Симуляторы (iOS) — это программные копии мобильных устройств, запускаемые на сервере или компьютере разработчика.

  • Плюсы: Бесплатны, мгновенно создаются и уничтожаются, легко интегрируются в CI/CD пайплайны.
  • Минусы: Не могут проверить работу Bluetooth, NFC, точную цветопередачу камеры или расход батареи. Симулятор iOS вообще не имеет ядра реальной ОС, он лишь транслирует команды в macOS.
  • Фермы реальных устройств (Device Farms) — это физические смартфоны, подключенные кабелями к серверам. Инженер может удаленно устанавливать на них приложения и запускать тесты.

    В современной автоматизации применяется гибридный подход: 90% ежедневных прогонов при создании новых веток в Git (Pull Requests) выполняются на быстрых эмуляторах. Оставшиеся 10% (регрессионное тестирование перед релизом в App Store / Google Play) запускаются на ферме реальных устройств.

    Архитектура Appium

    Индустриальным стандартом для автоматизации мобильных приложений является Appium. Это кроссплатформенный инструмент с открытым исходным кодом, который позволяет писать тесты на Kotlin, используя философию WebDriver.

    Appium работает по клиент-серверной архитектуре:

  • Клиент (Тесты на Kotlin): Ваш код формирует команды (например, «найти кнопку и кликнуть») и отправляет их в виде HTTP-запросов в формате JSON.
  • Appium Server: Node.js сервер, который принимает эти HTTP-запросы.
  • Драйвер платформы: Appium Server переводит универсальные команды в специфичные инструкции для конкретной ОС. Для Android используется UIAutomator2 (нативный фреймворк от Google), для iOS — XCUITest (от Apple).
  • Устройство: Нативный фреймворк выполняет действие на эмуляторе или реальном смартфоне.
  • !Схема архитектуры Appium: от кода на Kotlin через HTTP REST к нативным драйверам Android и iOS

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

    Настройка инфраструктуры и Bash

    Для работы с Android-устройствами инженеру необходимо владеть утилитой ADB (Android Debug Bridge), которая вызывается через командную строку Bash.

    Базовые команды Bash для работы с ADB:

    Запуск самого Appium сервера также автоматизируется через Bash-скрипты в CI/CD системах:

    Автоматизация на Kotlin: Практика

    Для интеграции Appium в проект на Kotlin необходимо добавить зависимости в build.gradle.kts:

    Настройка Capabilities

    Чтобы Appium понял, какое устройство запустить и какое приложение установить, используются Desired Capabilities — набор ключ-значение.

    Стратегии поиска элементов (Локаторы)

    В мобильном тестировании поиск элементов отличается от веб-версий. Использование XPath в Appium считается антипаттерном, так как парсинг XML-дерева мобильного экрана занимает много времени и сильно замедляет тесты.

    Лучшие практики локаторов в порядке приоритета:

  • Accessibility ID (content-desc в Android, accessibilityIdentifier в iOS). Это атрибуты, созданные для программ чтения с экрана (для слабовидящих). Их использование делает тесты быстрыми и независимыми от языка интерфейса.
  • ID (resource-id). Уникальные идентификаторы элементов.
  • Class Name. Поиск по типу элемента (например, android.widget.TextView).
  • Реализация Page Object на Kotlin

    Используем мощь языка Kotlin (Scope-функции) для создания элегантного Page Object для экрана авторизации.

    Написание автотеста (Паттерн AAA)

    Теперь объединим инфраструктуру и Page Object в читаемый тест, следуя паттерну Arrange-Act-Assert.

    Интеграция с Git и Allure

    При работе в команде код автотестов хранится в системе контроля версий Git. Важно правильно настроить файл .gitignore, чтобы не отправлять в репозиторий тяжелые APK-файлы и скомпилированные классы:

    Для анализа упавших тестов используется Allure Framework. В мобильном тестировании недостаточно просто сохранить скриншот. При падении теста фреймворк должен автоматически вытягивать логи устройства (через adb logcat) и прикреплять их к Allure-отчету. Это позволяет разработчикам понять, упал ли тест из-за изменения UI, или приложение действительно скрашилось на уровне операционной системы (например, получив NullPointerException в Java-коде Android).

    Переход от ручного тестирования мобильных приложений к автоматизации на Kotlin с использованием Appium требует понимания как архитектуры мобильных ОС, так и принципов построения стабильного кода. Отказ от нестабильных локаторов (XPath), использование явных ожиданий и грамотное управление состоянием устройства через Bash — ключи к созданию надежного тестового покрытия в финтехе.

    13. Автоматизация мобильных приложений на Kotlin

    Автоматизация мобильных приложений на Kotlin

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

    Использование языка программирования Kotlin в связке с фреймворком Appium позволяет создавать надежные, читаемые и легко поддерживаемые автотесты. Kotlin, будучи официально рекомендуемым языком для разработки под Android, предоставляет инженерам по автоматизации мощные инструменты: функции-расширения, безопасную работу с null-ссылками и лаконичный синтаксис.

    Преимущества Kotlin перед Java в мобильной автоматизации

    Исторически большинство тестов для Appium писалось на Java. Однако переход на Kotlin позволяет сократить объем шаблонного кода (boilerplate) почти в два раза и избежать множества типичных ошибок.

    Безопасность работы с Null (Null Safety)

    В мобильном тестировании элементы интерфейса часто появляются на экране с задержкой или могут вообще отсутствовать (например, всплывающее окно с предложением оформить кредитную карту, которое показывается только новым клиентам). В Java попытка взаимодействия с отсутствующим элементом приводит к критической ошибке NullPointerException, которая обрушивает весь тест.

    Система типов Kotlin различает ссылки, которые могут содержать null (nullable), и те, которые не могут (non-null). Это заставляет инженера явно обрабатывать ситуации отсутствия элемента на этапе написания кода.

    Функции-расширения (Extension Functions)

    Функции-расширения позволяют добавлять новые методы к существующим классам без необходимости наследоваться от них. В контексте Appium это идеальный инструмент для создания собственных «умных ожиданий».

    Вместо того чтобы каждый раз писать громоздкие конструкции WebDriverWait, мы можем «научить» сам класс AndroidDriver или WebElement ждать своего появления.

    Продвинутый Page Object Model на Kotlin

    Паттерн Page Object Model (POM) — это стандарт индустрии, который предписывает разделять логику поиска элементов на экране и саму бизнес-логику теста. В Kotlin этот паттерн раскрывается по-новому благодаря Scope-функциям (apply, with, run).

    Рассмотрим экран перевода средств по номеру телефона (СБП). Нам нужно ввести номер, сумму, сообщение и нажать кнопку перевода.

    В самом тесте использование такого Page Object выглядит как читаемый текст на английском языке, что полностью соответствует принципам BDD (Behavior-Driven Development):

    !Схема архитектуры тестового фреймворка: от автотестов на Kotlin через сервер Appium к мобильному устройству и проверке данных в базе данных

    Специфичные финтех-сценарии в мобильном тестировании

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

    1. Биометрическая аутентификация (FaceID / TouchID)

    Вход по отпечатку пальца или скану лица — базовая функция любого современного финтех-приложения. Автотест не может приложить реальный палец к эмулятору, но Appium позволяет отправить системную команду, имитирующую успешное или неуспешное распознавание.

    2. Перехват SMS и OTP (One-Time Password)

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

  • Инфраструктурный (Shift-Left): Тестовый контур бэкенда настраивается так, чтобы для тестовых номеров телефонов всегда подходил статический код (например, 0000), либо код записывается в тестовую базу данных, откуда автотест его считывает.
  • Мобильный (Чтение SMS): Если первый вариант недоступен, Appium может прочитать входящие уведомления устройства.
  • Пример чтения SMS через Appium (Android):

    3. Тестирование нестабильной сети

    Что произойдет, если клиент нажмет кнопку «Оплатить», и в этот момент пропадет интернет? Приложение не должно упасть, а транзакция не должна отправиться дважды при восстановлении связи. Это проверяется путем переключения состояний сети прямо во время теста.

    Интеграция с базами данных (SQL)

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

    Если автотест перевел 5000 рублей, мы должны подключиться к БД и проверить, что баланс отправителя уменьшился, а статус транзакции в таблице transactions изменился на COMPLETED.

    Для работы с базами данных в Kotlin отлично подходит фреймворк JetBrains Exposed. Он позволяет писать SQL-запросы в виде безопасного Kotlin-кода.

    text

    Исключаем скомпилированные приложения

    *.apk *.ipa *.app

    Исключаем локальные настройки среды

    local.properties .idea/ build/

    Исключаем результаты тестов

    allure-results/ kotlin import io.qameta.allure.Allure import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.api.extension.TestWatcher import org.openqa.selenium.OutputType import org.openqa.selenium.TakesScreenshot import java.io.ByteArrayInputStream

    class ScreenshotOnFailureExtension : TestWatcher { override fun testFailed(context: ExtensionContext, cause: Throwable) { // Получаем доступ к драйверу (предполагается, что он хранится в базовом классе теста) val driver = getDriverFromContext(context) if (driver is TakesScreenshot) { val screenshot = driver.getScreenshotAs(OutputType.BYTES) // Прикрепляем скриншот к Allure отчету Allure.addAttachment("Скриншот при падении", "image/png", ByteArrayInputStream(screenshot), ".png") } } } ``

    Подключив это расширение к тестовому классу аннотацией @ExtendWith(ScreenshotOnFailureExtension::class)`, вы получите красивый HTML-отчет, где к каждому упавшему тесту приложен снимок экрана мобильного телефона в момент сбоя. Это радикально ускоряет процесс локализации дефектов.

    Автоматизация мобильных приложений на Kotlin — это комплексный процесс, объединяющий знание архитектуры мобильных ОС, умение работать с базами данных, владение командной строкой и глубокое понимание паттернов проектирования. Использование современных возможностей Kotlin позволяет сделать этот процесс эффективным, а код тестов — надежным и легко читаемым.

    14. Основы реляционных баз данных

    Основы реляционных баз данных

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

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

    Реляционные базы данных (RDBMS) — это фундамент подавляющего большинства финансовых систем в мире. Понимание принципов их работы и умение писать SQL-запросы — критически важный навык для QA Automation Engineer, позволяющий готовить тестовые данные и проводить глубокие проверки (Asserts) на уровне бэкенда.

    Что такое реляционная база данных

    Реляционная база данных — это способ организации информации в виде набора связанных двумерных таблиц. Концепция была предложена в 1970 году исследователем из IBM Эдгаром Коддом и с тех пор стала индустриальным стандартом.

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

    Основные компоненты таблицы

    Каждая таблица в реляционной базе данных (также называемая отношением или relation) состоит из двух главных элементов:

  • Столбцы (Атрибуты): Определяют структуру данных. Каждый столбец имеет уникальное имя и строгий тип данных (например, целое число, текст, дата, дробное число). В таблице users могут быть столбцы id, name, email, registration_date.
  • Строки (Записи или Кортежи): Содержат фактические данные. Каждая строка представляет собой один конкретный объект (одного пользователя, одну транзакцию). На пересечении строки и столбца всегда находится ровно одно атомарное значение.
  • > Реляционная модель требует, чтобы данные были нормализованы. Нормализация — это процесс организации данных, направленный на устранение избыточности (дублирования) и обеспечение логической связности.

    Ключи и связи: как таблицы общаются друг с другом

    Сила реляционных баз данных заключается в слове «реляционные» (от англ. relation — связь, отношение). Данные не сваливаются в одну гигантскую таблицу. Они разделяются на логические блоки, которые затем связываются между собой с помощью системы ключей.

    Первичный ключ (Primary Key)

    Первичный ключ (PK) — это столбец (или набор столбцов), который уникально идентифицирует каждую строку в таблице.

    Представьте таблицу клиентов банка. Имя и фамилия не могут быть первичным ключом, так как в банке могут обслуживаться два Ивана Иванова. Номер паспорта подходит лучше, но он может измениться при утере документа. Поэтому базы данных обычно используют суррогатные ключи — уникальные идентификаторы (ID), генерируемые самой системой (например, user_id = 10456).

    Правила первичного ключа:

  • Должен быть абсолютно уникальным для каждой строки.
  • Не может содержать пустое значение (NULL).
  • Значение ключа не должно меняться со временем.
  • Внешний ключ (Foreign Key)

    Внешний ключ (FK) — это столбец в одной таблице, который ссылается на первичный ключ в другой таблице. Именно внешний ключ создает логическую связь.

    Рассмотрим пример из финтеха. У нас есть две таблицы: users (клиенты) и accounts (банковские счета).

    | users (Клиенты) | | | |---|---|---| | user_id (PK) | full_name | phone | | 1 | Анна Смирнова | +79991112233 | | 2 | Иван Иванов | +79994445566 |

    | accounts (Счета) | | | | |---|---|---|---| | account_id (PK) | user_id (FK) | balance | currency | | 101 | 1 | 50000.00 | RUB | | 102 | 1 | 150.50 | USD | | 103 | 2 | 10000.00 | RUB |

    В таблице accounts столбец user_id является внешним ключом. Он указывает, какому именно клиенту принадлежит счет. Благодаря этому мы видим, что у Анны (ID 1) есть два счета (рублевый и долларовый), а у Ивана (ID 2) — только один.

    !Схема реляционной базы данных финтех-приложения, показывающая связи между таблицами Users, Accounts и Transactions

    ACID: Фундамент надежности финансовых систем

    Почему банки не хранят данные в обычных текстовых файлах? Причина кроется в наборе требований к транзакционным системам, известном как аббревиатура ACID.

    Atomicity (Атомарность)

    Транзакция выполняется целиком или не выполняется вообще. Не может быть промежуточного состояния.

    Если Иван переводит Анне 5000 рублей, база данных должна выполнить две операции:

  • Вычесть 5000 рублей со счета Ивана.
  • Прибавить 5000 рублей на счет Анны.
  • Если после первого шага сервер потеряет питание, деньги Ивана исчезнут, но к Анне не поступят. Атомарность гарантирует, что в случае любого сбоя система откатится (Rollback) к исходному состоянию. Деньги либо переведутся полностью, либо останутся у Ивана.

    Consistency (Согласованность)

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

    Например, в базе данных установлено правило: баланс дебетового счета не может быть меньше нуля (). Если Иван попытается перевести 15000 рублей, имея на счету только 10000, база данных отклонит такую транзакцию на уровне ядра, защищая систему от появления отрицательных балансов.

    Isolation (Изолированность)

    Параллельные транзакции не должны влиять друг на друга.

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

    Durability (Долговечность)

    Если база данных сообщила, что транзакция успешно завершена (Committed), эти изменения сохранятся навсегда, даже если через секунду в дата-центре выключат электричество. Данные записываются на энергонезависимые носители (жесткие диски или SSD) до того, как система подтвердит успех операции.

    Основы SQL для автоматизатора тестирования

    Для взаимодействия с реляционными базами данных используется язык SQL (Structured Query Language). В автоматизации тестирования SQL применяется для двух главных задач:

  • Подготовка данных (Arrange): Создание тестовых пользователей, начисление баланса перед выполнением теста.
  • Проверка результатов (Assert): Чтение данных из таблиц после выполнения действий в UI или API для подтверждения корректности работы системы.
  • SQL базируется на четырех основных операциях, известных как CRUD (Create, Read, Update, Delete).

    1. Чтение данных (SELECT)

    Самая частая команда в арсенале тестировщика. Позволяет извлекать данные по определенным критериям.

    2. Создание данных (INSERT)

    Используется для добавления новых записей. В автотестах часто применяется для генерации уникальных тестовых сущностей в обход UI (что значительно ускоряет выполнение тестов).

    3. Обновление данных (UPDATE)

    Позволяет изменять существующие записи. Полезно для перевода тестового пользователя в определенное состояние (например, заблокировать карту для проверки негативного сценария).

    4. Удаление данных (DELETE)

    Используется для очистки базы данных после выполнения тестов (Teardown), чтобы тестовые данные не накапливались и не влияли на последующие запуски.

    Объединение таблиц: SQL JOINs

    Часто данные, необходимые для проверки, лежат в разных таблицах. Чтобы получить полную картину, таблицы нужно объединить. Для этого используется оператор JOIN.

    Предположим, мы хотим получить список имен клиентов и балансы их счетов. Имена лежат в таблице users, а балансы — в accounts.

    Существует несколько типов объединений:

    * INNER JOIN: Возвращает только те строки, для которых есть совпадения в обеих таблицах. Если у пользователя нет ни одного счета, он не попадет в результат. * LEFT JOIN: Возвращает ВСЕ строки из левой таблицы (users), даже если для них нет совпадений в правой таблице (accounts). В этом случае вместо баланса будет выведено значение NULL. * RIGHT JOIN: Работает аналогично LEFT JOIN, но возвращает все строки из правой таблицы.

    !Интерактивная визуализация SQL JOIN — показывает, как строки из двух таблиц объединяются при использовании INNER JOIN и LEFT JOIN

    > Важное правило: При использовании JOIN всегда указывайте условие объединения (ключевое слово ON). Если забыть это сделать, база данных выполнит декартово произведение (CROSS JOIN) — каждая строка первой таблицы соединится с каждой строкой второй таблицы.

    Количество строк при ошибочном CROSS JOIN вычисляется по формуле:

    Где — количество строк в первой таблице, а — во второй. Если в таблице пользователей 10 000 записей, а в таблице счетов 20 000, ошибочный запрос вернет 200 000 000 строк, что может привести к падению базы данных (Out of Memory).

    Интеграция баз данных с автотестами на Kotlin

    В экосистеме Kotlin для работы с базами данных редко пишут «голые» SQL-запросы в виде строк (JDBC). Это чревато синтаксическими ошибками, которые обнаруживаются только во время выполнения (Runtime).

    Современный подход — использование ORM (Object-Relational Mapping) или SQL DSL фреймворков, таких как JetBrains Exposed. Этот инструмент позволяет описывать таблицы как объекты Kotlin и писать запросы, используя строгую типизацию языка.

    Пример проверки баланса после перевода средств в автотесте на Kotlin:

    Использование JetBrains Exposed дает автодополнение кода в IDE (IntelliJ IDEA) и проверку типов на этапе компиляции. Если разработчики переименуют столбец balance в amount и обновят схему, ваш код просто не скомпилируется, что позволит исправить тест до его запуска в CI/CD конвейере.

    Место баз данных в инфраструктуре тестирования

    Работа с базами данных тесно переплетается с другими инструментами QA Automation инженера:

    * Bash и CI/CD: Перед запуском тестов на сервере (например, в GitLab CI), Bash-скрипты часто используются для развертывания чистой тестовой базы данных в Docker-контейнере и накатывания миграций (создания таблиц). * Git: SQL-скрипты для подготовки тестовых данных (файлы .sql) должны храниться в системе контроля версий вместе с кодом автотестов. Это гарантирует, что любой инженер, скачавший репозиторий, получит идентичный набор данных. * Allure: При падении теста из-за несовпадения данных в БД, хорошей практикой является логирование выполненного SQL-запроса и фактического ответа базы данных в отчет Allure. Это избавляет инженера от необходимости вручную подключаться к БД для расследования причин падения.

    Реляционные базы данных — это строгий, надежный и предсказуемый механизм хранения информации. Для QA-инженера в финтехе умение работать с SQL и интегрировать проверки БД в код на Kotlin — это переход от поверхностного тестирования интерфейсов к глубокому контролю качества всей архитектуры приложения.

    15. SQL-запросы для тестировщиков

    Представьте ситуацию: вы автоматизируете тестирование процесса выдачи кредита в банковском приложении. Ваш автотест отправляет API-запрос на создание заявки, и сервер возвращает статус-код 200 OK с сообщением «Заявка принята». Тест пройден?

    С точки зрения поверхностного тестирования — да. Но с точки зрения обеспечения качества финтех-продукта — нет. Сервер мог вернуть успешный ответ, но из-за ошибки в логике бэкенда записать в базу данных процентную ставку 1.5% вместо 15.0%. Когда клиент подпишет договор, банк потеряет миллионы.

    Единственный способ гарантировать корректность работы системы — проверить источник правды (Source of Truth). В абсолютном большинстве случаев этим источником является реляционная база данных. Умение писать сложные SQL-запросы позволяет QA-инженеру не только проверять конечный результат (Assert), но и мгновенно подготавливать систему к тесту (Arrange), минуя медленный пользовательский интерфейс.

    Анатомия SQL-запроса: порядок выполнения

    SQL (Structured Query Language) — это декларативный язык. Вы описываете, какой результат хотите получить, а система управления базами данных (СУБД, например PostgreSQL или MySQL) сама решает, как именно его вычислить.

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

    Порядок написания:

  • SELECT (что вывести)
  • FROM (откуда взять)
  • WHERE (как отфильтровать строки)
  • GROUP BY (как сгруппировать)
  • HAVING (как отфильтровать группы)
  • ORDER BY (как отсортировать)
  • Порядок выполнения движком БД:

  • FROM — база находит нужную таблицу (или объединяет несколько таблиц).
  • WHERE — отбрасываются строки, не подходящие под условие.
  • GROUP BY — оставшиеся строки собираются в группы.
  • HAVING — отбрасываются целые группы, не прошедшие проверку.
  • SELECT — формируется финальный набор столбцов для вывода.
  • ORDER BY — готовый результат сортируется перед отправкой пользователю.
  • Понимание этого порядка объясняет, почему нельзя использовать псевдонимы столбцов (alias), заданные в SELECT, внутри условия WHERE — на момент выполнения WHERE база данных еще не знает о существовании SELECT.

    Продвинутая фильтрация данных (WHERE)

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

    Помимо базовых операторов сравнения (, , ), SQL предлагает мощные инструменты для работы с множествами и текстом.

    Оператор IN

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

    Оператор LIKE

    Используется для поиска по шаблону в текстовых строках. Символ % заменяет любое количество символов, а _ — ровно один символ. Это незаменимо при поиске специфичных ошибок в логах, которые пишутся в БД.

    Работа с NULL

    В реляционных базах данных NULL означает «отсутствие значения» (неизвестность). NULL не равен нулю или пустой строке. Любое сравнение с NULL (например, balance = NULL) вернет ложь. Для проверки на пустоту используются специальные операторы IS NULL и IS NOT NULL.

    Агрегация данных: GROUP BY и HAVING

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

    Для этого используются агрегатные функции: * COUNT() — подсчет количества строк. * SUM() — сумма значений. * AVG() — среднее арифметическое. * MAX() и MIN() — максимальное и минимальное значения.

    Если применить агрегатную функцию ко всей таблице, мы получим одно число. Но чаще нам нужно получить агрегацию в разрезе каких-то категорий. Здесь вступает в игру GROUP BY.

    Если после группировки нам нужно отфильтровать результаты (например, оставить только тех пользователей, кто потратил больше 100 000 рублей), мы не можем использовать WHERE. Оператор WHERE работает со строками до группировки. Для фильтрации после группировки используется HAVING.

    !Интерактивная визуализация GROUP BY и HAVING — показывает, как исходная таблица транзакций сначала фильтруется по статусу, затем разбивается на группы по пользователям, внутри групп вычисляется сумма, и в конце отбрасываются группы с суммой меньше заданного порога

    Вложенные запросы (Subqueries)

    Вложенный запрос (или подзапрос) — это запрос SELECT, находящийся внутри другого SQL-запроса. Он позволяет использовать результат одного вычисления как условие для другого.

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

    Мы не можем написать WHERE balance > AVG(balance), так как агрегатные функции нельзя использовать в WHERE. Решение — вложенный запрос:

    База данных сначала выполнит внутренний запрос (вычислит среднее значение, например, 45000), подставит это число на место скобок, и затем выполнит внешний запрос: WHERE balance > 45000.

    SQL в автоматизации: Подготовка данных (Arrange)

    Хотя SELECT используется для проверок, команды модификации данных (INSERT, UPDATE, DELETE) критически важны для подготовки тестового окружения.

    Создание тестовых данных через UI или API может занимать секунды или даже минуты. Если у вас 1000 тестов, это выльется в часы ожидания. Прямая запись в базу данных выполняется за миллисекунды.

    UPDATE для изменения состояний

    Частый кейс — тестирование поведения приложения при заблокированном аккаунте. Вместо того чтобы писать UI-тест, который логинится под админом, ищет пользователя и нажимает кнопку «Заблокировать», мы делаем это одним запросом:

    > Критическое правило: Никогда не выполняйте UPDATE или DELETE без условия WHERE. Забытое условие приведет к обновлению или удалению абсолютно всех строк в таблице. В тестовой среде это сломает тесты вашим коллегам, а на продакшене — станет катастрофой.

    DELETE для очистки (Teardown)

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

    Интеграция SQL и Kotlin: JetBrains Exposed

    В современной автоматизации на Kotlin мы не пишем SQL-запросы в виде обычных строк (String). Строки не проверяются компилятором: если вы опечатаетесь в названии столбца (SELECT balanc FROM...), вы узнаете об этом только во время выполнения теста, когда он упадет.

    Для безопасной работы с БД в экосистеме Kotlin используется фреймворк JetBrains Exposed. Он предоставляет SQL DSL (Domain-Specific Language), позволяющий писать запросы на чистом Kotlin.

    !Схема взаимодействия автотеста с системой: Тест отправляет HTTP-запрос к API, API взаимодействует с Базой Данных. Затем Тест напрямую обращается к Базе Данных через JetBrains Exposed для верификации результата, минуя API.

    Рассмотрим полный пример автотеста API перевода средств с проверкой в базе данных. Тест использует паттерн AAA (Arrange, Act, Assert).

    В этом примере Accounts.update и Accounts.select — это методы Kotlin, которые Exposed автоматически транслирует в безопасные SQL-запросы. Если схема БД изменится, код просто не скомпилируется.

    Инфраструктура: Git, Bash и Allure

    Работа с базами данных в автоматизации не ограничивается написанием кода. Она тесно связана с инфраструктурными инструментами:

  • Bash и Docker: Перед запуском набора тестов в CI/CD (Continuous Integration), Bash-скрипт поднимает чистую базу данных в Docker-контейнере. Это гарантирует, что тесты всегда выполняются в предсказуемой среде, изолированной от других разработчиков.
  • Git: Скрипты для создания таблиц (миграции) и наполнения их базовыми справочными данными хранятся в репозитории Git вместе с кодом тестов. Это позволяет любому QA-инженеру развернуть проект локально одной командой.
  • Allure Reports: При падении теста на этапе проверки БД (Assert), хорошей практикой является логирование выполненного SQL-запроса и фактического состояния строки в отчет Allure. Это экономит время: инженеру не нужно вручную подключаться к базе через консоль, чтобы понять, почему баланс не сошелся.
  • Владение SQL переводит QA-инженера из статуса «пользователя, нажимающего кнопки» в статус технического специалиста, способного контролировать систему на самом глубоком архитектурном уровне. Это особенно критично в финтехе, где цена ошибки в данных измеряется реальными деньгами.

    16. Построение отчетов с помощью Allure

    В предыдущей статье мы разобрали, как использовать SQL-запросы для подготовки тестовых данных и проверки состояния базы данных. Вы научились находить источник правды и верифицировать финансовые транзакции на самом глубоком уровне. Однако найти баг или успешно прогнать тысячу автотестов — это лишь половина дела. Вторая, не менее важная задача QA Automation инженера — грамотно презентовать результаты своей работы команде.

    Представьте ситуацию: ночной прогон автотестов завершился, и из 500 тестов упало 45. Вы открываете стандартный лог CI/CD системы и видите бесконечную простыню текста с ошибками вроде NullPointerException или AssertionError: expected <200> but was <500>. Чтобы понять, какие именно бизнес-функции сломались (перестали отправляться SMS с OTP-кодом или отвалился шлюз оплаты?), вам придется вручную анализировать каждый лог. А теперь представьте, что этот лог нужно показать Product Owner-у или менеджеру проекта. Для них этот текст — просто белый шум.

    Здесь на сцену выходит Allure Framework — инструмент, который переводит технические логи автотестов на язык, понятный бизнесу.

    Что такое Allure Framework и зачем он нужен

    Allure Framework — это гибкий многоязычный инструмент для создания визуально привлекательных и интерактивных отчетов о тестировании. Он был разработан компанией Qameta Software и быстро стал индустриальным стандартом благодаря своей наглядности и простоте интеграции.

    Главная задача Allure — разделить процесс тестирования на два независимых этапа:

  • Сбор данных (Execution): Во время выполнения тестов фреймворк (например, JUnit 5) генерирует сырые данные в формате JSON или XML. В этих файлах фиксируется, какой тест запустился, сколько времени он шел и с каким результатом завершился.
  • Генерация отчета (Generation): Специальная консольная утилита берет эти сырые файлы и собирает из них красивый HTML-сайт с графиками, диаграммами и подробной статистикой.
  • !Схема процесса генерации отчета Allure: от запуска автотестов до получения итогового HTML-документа

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

    В финтех-отрасли Allure решает три критические проблемы: * Прозрачность для бизнеса: Менеджеры видят круговые диаграммы успешности (Pass Rate). Формула расчета проста: . Если Pass Rate падает ниже 95%, релиз останавливается. * Ускорение отладки: Тестировщики получают доступ к скриншотам, логам HTTP-запросов и ответам базы данных прямо в отчете, без необходимости лезть в консоль сервера. Анализ стабильности: Allure умеет выявлять Flaky-тесты* (моргающие тесты) — тесты, которые то проходят, то падают без изменения кода.

    Иерархия тестов: язык бизнеса

    Чтобы отчет был читаемым, тесты нужно структурировать. В Allure используется подход, заимствованный из методологии BDD (Behavior-Driven Development). Он позволяет группировать тесты по бизнес-логике с помощью специальных аннотаций.

    Существует три основных уровня иерархии:

  • @Epic (Эпик) — самый крупный модуль системы. Например, «Кредитование» или «Платежи».
  • @Feature (Функция) — конкретная функциональность внутри эпика. Например, «Ипотека» или «Переводы по номеру телефона».
  • @Story (Пользовательская история) — конкретный сценарий использования. Например, «Расчет процентной ставки для зарплатного клиента».
  • Рассмотрим, как это выглядит в коде на Kotlin:

    Благодаря этим аннотациям, в отчете Allure появится вкладка Behaviors (Поведение). Если упадет тест на превышение лимита, менеджер увидит красную отметку не просто на абстрактном классе SbpTransferTests, а в конкретной бизнес-ветке: Платежи и переводы → Переводы по СБП → Превышение суточного лимита. Это позволяет мгновенно оценить бизнес-риски.

    !Интерактивная структура отчета Allure — показывает, как тесты группируются по Эпикам, Фичам и Сторям, позволяя быстро найти упавший бизнес-сценарий

    Детализация: Шаги (@Step) и Вложения (@Attachment)

    Знать, какой тест упал — полезно. Знать, на каком именно этапе он упал — бесценно. Для детализации процесса выполнения теста Allure предоставляет аннотацию @Step.

    Шаг (Step) — это логически завершенное действие внутри теста. Вместо того чтобы писать монолитный код на 50 строк, мы разбиваем его на методы и помечаем их аннотацией @Step. Allure перехватывает вызовы этих методов и строит древовидную структуру выполнения.

    Обратите внимание на синтаксис {userId} и {amount} в названиях шагов. Allure умеет автоматически подставлять значения аргументов функции прямо в текст отчета. Это избавляет от необходимости хардкодить данные в названиях.

    Вложения (Attachments)

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

    Аннотация @Attachment позволяет прикрепить к отчету любой файл: текстовый лог, JSON-ответ от сервера, скриншот веб-страницы или даже видео прохождения теста.

    В современных библиотеках, таких как Selenide (для UI) или REST Assured (для API), уже есть встроенные интеграции (Listeners) для Allure. Достаточно добавить одну строчку конфигурации, и фреймворк будет автоматически прикреплять скриншоты при падении UI-тестов или логировать все HTTP-запросы и ответы.

    > «Читаемость тестов достигается за счет того, что демонстрируемые пользователю результаты не зависят от языка и фреймворка тестов. Такая независимость — результат изначального структурного решения». > > habr.com

    Интеграция с инфраструктурой: Gradle и Bash

    Чтобы вся эта магия заработала, Allure нужно интегрировать в систему сборки проекта. В экосистеме Kotlin стандартом де-факто является Gradle.

    В файле build.gradle.kts необходимо подключить плагин Allure и указать зависимости:

    После настройки проекта процесс генерации отчета сводится к работе с командной строкой Bash. Это особенно важно для запуска тестов на удаленных серверах (CI/CD), где нет графического интерфейса.

    Процесс состоит из двух команд:

  • ./gradlew clean test — эта команда запускает компиляцию кода и выполнение тестов. В процессе работы в директории проекта создается папка build/allure-results. Именно туда складываются те самые сырые JSON-файлы с результатами.
  • ./gradlew allureServe — эта команда берет сырые данные из папки allure-results, генерирует из них красивый HTML-сайт и автоматически поднимает локальный веб-сервер для его отображения в браузере.
  • Если вам нужно просто сгенерировать HTML-файлы для отправки архивом (без запуска сервера), используется команда ./gradlew allureReport. Готовый сайт появится в папке build/reports/allure-report.

    Продвинутые возможности: Flaky-тесты и связи с TMS

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

    Allure умеет отслеживать историю запусков. Если тест вчера прошел, сегодня упал, а после перезапуска снова прошел — Allure пометит его специальной иконкой бомбочки (Flaky). Это сигнал для QA-инженера: тест нужно переписать, добавив явные ожидания, или изолировать тестовые данные.

    Еще одна мощная функция — интеграция с системами управления тестированием (TMS), такими как Jira или TestRail. С помощью аннотаций @Issue и @TmsLink можно связать автотест с конкретным баг-репортом или ручным тест-кейсом.

    При генерации отчета Allure превратит эти аннотации в кликабельные ссылки. Если тест упадет, разработчик сможет в один клик перейти в Jira по номеру FIN-1042 и прочитать подробное описание проблемы.

    В крупных компаниях часто идут дальше и используют коммерческую версию Allure TestOps. Она позволяет не просто генерировать отчеты, но и управлять всем процессом тестирования, объединяя ручные и автоматизированные тесты в едином пространстве, что позволяет отказаться от устаревших TMS.

    Внедрение Allure Framework превращает автотесты из «черного ящика», понятного только автоматизаторам, в прозрачный инструмент контроля качества, которому доверяет весь бизнес.

    17. Особенности тестирования в финтехе

    Особенности тестирования в финтехе

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

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

    Смещение фокуса: почему API важнее UI

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

    Сердцем любого банка является АБС (Автоматизированная банковская система) или Core Banking System. Это гигантская база данных и набор сервисов, которые хранят информацию о счетах и балансах. Мобильное приложение общается с АБС через программные интерфейсы — API.

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

  • Это невероятно медленно.
  • UI-тесты нестабильны (Flaky) из-за анимаций и задержек рендеринга.
  • Мы не сможем проверить граничные случаи (например, отправку отрицательной суммы), так как UI обычно блокирует такой ввод.
  • Поэтому в финтехе пирамида тестирования строго соблюдается: 80% усилий автоматизатора направлено на API-тесты. Мы отправляем HTTP-запросы напрямую к серверу, минуя графический интерфейс.

    Рассмотрим пример на Kotlin с использованием библиотеки REST Assured. Мы проверяем API расчета комиссии за перевод. Формула расчета комиссии проста: , где — итоговая комиссия, — сумма перевода, — процентная ставка.

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

    Идемпотентность транзакций

    Вернемся к примеру с двойным списанием на кассе. Эта проблема возникает, когда система не обладает свойством идемпотентности.

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

    В архитектуре REST разные HTTP-методы имеют разные стандарты идемпотентности:

    | HTTP Метод | Назначение | Идемпотентен по стандарту? | Поведение при повторе | | :--- | :--- | :--- | :--- | | GET | Получение баланса | Да | Баланс просто возвращается снова | | PUT | Обновление профиля | Да | Данные перезаписываются теми же значениями | | DELETE | Закрытие счета | Да | Счет удаляется, повторный запрос вернет 404 | | POST | Создание перевода | Нет | Каждый запрос создает новый перевод |

    Поскольку финансовые транзакции создаются методом POST, разработчикам приходится искусственно делать его идемпотентным. Для этого используется специальный HTTP-заголовок — Idempotency-Key (Ключ идемпотентности).

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

    Задача QA Automation инженера — обязательно покрывать этот механизм автотестами.

    !Интерактивная схема работы ключа идемпотентности

    Целостность данных и принцип ACID

    В финтехе база данных — это абсолютный источник правды. Если API ответило статусом 200 OK, это еще не значит, что деньги реально дошли. Тестировщик обязан заглянуть в базу данных и проверить состояние таблиц с помощью SQL-запросов.

    Финансовые базы данных (обычно это реляционные СУБД, такие как PostgreSQL или Oracle) строятся на фундаментальном принципе ACID.

    ACID — это акроним, описывающий четыре требования к транзакционной системе:

  • Atomicity (Атомарность): Транзакция выполняется целиком или не выполняется вообще. Перевод денег — это два шага: списать у отправителя (UPDATE accounts SET balance = balance - 100) и зачислить получателю (UPDATE accounts SET balance = balance + 100). Если сервер упадет между этими шагами, база данных отменит (Rollback) первый шаг. Деньги не «зависнут» в воздухе.
  • Consistency (Согласованность): Транзакция не может нарушить правила базы данных. Например, баланс не может стать отрицательным, если это не кредитный счет.
  • Isolation (Изолированность): Если два человека одновременно переводят вам деньги, эти операции не должны мешать друг другу и перезаписывать результаты.
  • Durability (Долговечность): Если транзакция подтверждена, она сохраняется навсегда, даже если в дата-центре отключится электричество.
  • В автотестах на Kotlin для работы с БД мы используем фреймворк JetBrains Exposed. Он позволяет писать SQL-запросы прямо в коде Kotlin, обеспечивая строгую типизацию.

    > «Тестирование в финтехе требует параноидального отношения к данным. Мы не верим ответам API, мы верим только записям в таблицах транзакций и логам аудита». > > habr.com

    Изоляция и мокирование внешних систем

    Ни один современный банк не работает в вакууме. Приложение постоянно общается с десятками внешних систем: * Платежные шлюзы (Visa, Mastercard, МИР) * Бюро кредитных историй * Государственные реестры (налоговая, порталы госуслуг) * Системы антифрода (защита от мошенников)

    !Схема архитектуры финтех-приложения с внешними интеграциями

    Как автоматизировать тестирование перевода по номеру карты Visa, если мы не можем реально переводить деньги при каждом запуске тестов (а их могут быть тысячи в день)? Более того, что делать, если тестовый контур Visa сегодня недоступен?

    Здесь применяется техника Мокирования (Mocking). Мок — это программа-заглушка, которая имитирует поведение реальной внешней системы.

    Вместо того чтобы отправлять запрос в реальный антифрод, наш тестовый сервер отправляет запрос в локальный Mock-сервер (например, WireMock). Мы можем запрограммировать этот мок так, чтобы на карту 4242 4242 4242 4242 он всегда отвечал 200 OK (транзакция разрешена), а на карту 5555 5555 5555 5555 отвечал 403 Forbidden (подозрение на мошенничество).

    Это позволяет QA-инженеру: * Тестировать систему абсолютно бесплатно. * Проверять негативные сценарии (что будет, если шлюз ответит ошибкой 503 Service Unavailable или будет отвечать дольше 10 секунд). * Сделать тесты независимыми от падений сторонних сервисов.

    Инфраструктура: Git, Bash и Allure

    В финтехе требования к безопасности и аудиту (Compliance) диктуют строгие правила работы с кодом автотестов. Вы не можете просто написать тест на своем ноутбуке и сказать «у меня все работает».

  • Git (Контроль версий): Код автотестов — это такой же производственный код. Он хранится в Git-репозитории. Каждое изменение проходит код-ревью. Если тест проверяет критическую функцию (например, расчет налогов), история коммитов в Git служит доказательством для аудиторов, что функция была протестирована конкретным инженером в конкретную дату.
  • Bash (Командная строка): Тесты запускаются не в IDE (IntelliJ IDEA), а на удаленных Linux-серверах в системах непрерывной интеграции (CI/CD). QA Automation инженер должен уметь написать Bash-скрипт, который скачает код, настроит переменные окружения (скрыв пароли от баз данных) и запустит команду ./gradlew test.
  • Allure (Отчетность): Когда прогоняется 5000 тестов, бизнесу нужен понятный результат. Allure Framework генерирует визуальные отчеты, где каждый тест привязан к бизнес-требованию. Если падает тест на оплату, Allure автоматически прикрепляет к отчету тело HTTP-запроса, ответ сервера и логи базы данных. Это позволяет разработчикам починить баг за минуты, а не часы.
  • Автоматизация тестирования в финтехе — это не просто написание скриптов, кликающих по кнопкам. Это глубокое понимание архитектуры, протоколов связи, устройства баз данных и принципов безопасности. Использование мощного и лаконичного языка Kotlin в связке с правильными инструментами позволяет создавать надежные щиты, защищающие деньги и данные миллионов пользователей.

    18. Проектирование тестового фреймворка на Kotlin

    Проектирование тестового фреймворка на Kotlin

    Когда начинающий специалист пишет свой первый автотест, это обычно выглядит как один длинный скрипт. В этом скрипте смешано всё: формирование HTTP-запроса, SQL-команды для подготовки данных, парсинг JSON-ответа и сами проверки (ассерты). Для одного теста такой подход работает отлично. Но когда тестов становится сто, тысяча или десять тысяч — поддержка такого кода превращается в кошмар. Изменение одного URL-адреса в API банка потребует переписывания сотен файлов.

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

    Трехуровневая архитектура фреймворка

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

    В коде это реализуется через трехуровневую архитектуру (Layered Architecture):

  • Слой тестов (Tests Layer): Здесь находятся классы с аннотациями тестового движка (например, JUnit 5). Этот слой не должен содержать технических деталей (URL, SQL-запросов, локаторов). Он описывает сценарий на языке, понятном бизнесу.
  • Слой бизнес-шагов (Steps Layer): Реализует паттерн Facade (Фасад). Здесь технические действия группируются в логические блоки. Например, шаг createBankClient() внутри себя может делать три HTTP-запроса и один INSERT в базу данных, но для слоя тестов это выглядит как одна простая команда.
  • Слой ядра (Core Layer): Самый низкоуровневый слой. Здесь живут HTTP-клиенты, подключения к базам данных, настройки логирования и драйверы браузеров.
  • !Схема трехуровневой архитектуры тестового фреймворка

    Такое разделение критически важно для финтеха. Если банк меняет протокол авторизации с Basic Auth на OAuth 2.0, вам нужно внести изменения только в одном месте — в слое ядра. Сами тесты останутся нетронутыми.

    Проектирование Ядра: Обертка над HTTP-клиентом

    В основе тестирования API лежит протокол HTTP. Мы отправляем запросы (GET, POST, PUT) и получаем ответы с определенными статус-кодами. Библиотека REST Assured отлично справляется с этой задачей, но использовать ее напрямую в тестах — плохая практика.

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

    Мы используем возможности Kotlin, в частности функции-расширения (Extension Functions), чтобы создать собственную обертку (Wrapper) над REST Assured.

    ``kotlin import io.restassured.RestAssured import io.restassured.specification.RequestSpecification import java.util.UUID

    // Объект-одиночка (Singleton) для базовой настройки клиента object ApiClient { // Функция-расширение для добавления стандартных финтех-заголовков private fun RequestSpecification.withFintechHeaders(): RequestSpecification { return this .header("Authorization", "Bearer T_{total}T_{seq}NT_{setup}T_{seq} = 50N = 5T_{setup} = 1$), общее время составит всего 11 минут. Именно поэтому в слое Ядра мы избегаем глобальных изменяемых переменных, которые могут вызвать состояние гонки (Race Condition) при параллельном запуске.

    Отчетность (Allure)

    Бизнесу не интересен код, ему нужны понятные отчеты. Фреймворк должен автоматически интегрироваться с Allure. Для этого в Kotlin используются аннотации @Step над методами в слое бизнес-шагов.

    Кроме того, в наш ApiClient можно добавить встроенный фильтр Allure для REST Assured (AllureRestAssured). Тогда при каждом вызове ApiClient.post()` фреймворк будет автоматически прикреплять к HTML-отчету точный URL, тело запроса и ответ сервера. Это сокращает время расследования упавших тестов с часов до минут.

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

    19. Непрерывная интеграция и доставка (CI/CD)

    Непрерывная интеграция и доставка (CI/CD)

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

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

    Концепция Shift-Left Testing

    Исторически тестирование находилось в самом конце жизненного цикла разработки ПО (SDLC). Сначала писали требования, потом код, и только перед релизом QA-инженеры начинали искать баги.

    Современный подход базируется на принципе Shift-Left Testing (Сдвиг влево). Если представить SDLC как линию слева направо (от идеи до релиза), то тестирование должно смещаться как можно левее, то есть начинаться на самых ранних этапах.

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

  • На этапе написания кода (локально) — исправление стоит минуты времени разработчика.
  • На этапе слияния веток (Merge Request) — требует времени на код-ревью и перезапуск автотестов.
  • На тестовом стенде (Staging) — привлекает внимание QA-инженера, требует заведения баг-репорта и смены контекста.
  • На проде (Production) — приводит к финансовым потерям, требует экстренного отката (Rollback) и написания отчетов об инциденте.
  • CI/CD — это главный технический инструмент, который делает концепцию Shift-Left возможной. Он гарантирует, что ни одна строчка кода не попадет в главную ветку без прохождения автоматических проверок.

    Анатомия CI/CD: Непрерывная интеграция и доставка

    Аббревиатура CI/CD состоит из двух неразрывно связанных понятий.

    Continuous Integration (CI) — непрерывная интеграция. Это практика, при которой разработчики часто (несколько раз в день) сливают свои изменения в общий репозиторий (Git). Каждое такое слияние автоматически запускает процесс сборки приложения и прогона модульных и API-тестов. Главная цель CI — быстро найти конфликты в коде и убедиться, что новая функция не сломала старые.

    Continuous Delivery (CD) — непрерывная доставка. Это логическое продолжение CI. Если код успешно собрался и прошел все тесты, система автоматически подготавливает его к развертыванию на серверах (тестовых или боевых).

    !Схема CI/CD пайплайна: от коммита разработчика через сборку и тестирование к автоматическому деплою и генерации отчетов

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

    Инфраструктура: Как это работает под капотом

    Для организации CI/CD используются специализированные системы: GitLab CI, Jenkins, GitHub Actions или TeamCity. Несмотря на разные интерфейсы, принцип их работы идентичен и тесно связан с инструментами, которые мы изучали ранее: Git и Bash.

  • Триггер (Git): Разработчик делает git push в удаленный репозиторий.
  • Перехват события: Система контроля версий отправляет сигнал (Webhook) на сервер CI/CD.
  • Выделение агента: Сервер CI/CD находит свободную виртуальную машину — Runner (Раннер) или Agent (Агент).
  • Выполнение команд (Bash): На этом раннере система пошагово выполняет Bash-команды, описанные в конфигурационном файле (обычно это YAML-файл).
  • Раннер — это, как правило, «чистая» Linux-машина без графического интерфейса. Именно поэтому QA Automation Engineer обязан уметь работать с командной строкой Bash. Вы не сможете открыть IntelliJ IDEA на раннере и нажать зеленую кнопку «Play». Все действия выполняются через терминал.

    Интеграция Kotlin-тестов в пайплайн

    Рассмотрим, как связать наш тестовый фреймворк на Kotlin с системой CI/CD на примере GitLab CI. Конфигурация описывается в файле .gitlab-ci.yml, который лежит в корне репозитория.

    В блоке script мы видим классические Bash-команды. Ключевая строка здесь — запуск системы сборки Gradle: ./gradlew clean test -Denv=staging -Dthreads=4.

    Передача параметров из CI в код

    Флаг -D в Gradle позволяет передавать системные свойства (System Properties) из командной строки Bash прямо в код на Kotlin. Это критически важно для гибкости фреймворка. Мы не должны хардкодить URL-адреса или пароли в коде тестов.

    В CI/CD системе мы можем настроить секретные переменные (например, пароль от тестовой БД), которые будут переданы в Bash, а оттуда — в Kotlin.

    Пример того, как слой ядра нашего фреймворка на Kotlin принимает эти параметры:

    Благодаря такому подходу, один и тот же скомпилированный код может тестировать локальную сборку, тестовый стенд или даже проверять доступность продакшена (Health Check) — достаточно лишь изменить параметры в настройках CI/CD.

    Математика CI/CD: Оптимизация времени выполнения

    Главное требование к CI/CD — скорость обратной связи. Если разработчик отправил код и ждет результатов тестов 2 часа, процесс разработки останавливается. В финтехе количество тестов быстро растет, поэтому их необходимо распараллеливать.

    Вспомним закон Амдала и формулу расчета времени параллельного выполнения:

    Где — общее время пайплайна, — время последовательного выполнения всех тестов, — количество потоков (раннеров), — время на подготовку окружения (скачивание Docker-образов, компиляция кода, поднятие тестовой БД).

    Если у нас 2000 API-тестов, которые последовательно идут 60 минут (), а подготовка окружения занимает 3 минуты (), то при запуске в 10 потоков () мы получим:

    Девять минут — это отличный показатель для CI/CD. Однако параллельный запуск обнажает главную проблему автоматизации — нестабильные тесты.

    Проблема Flaky-тестов в пайплайне

    Flaky tests (Мигающие тесты) — это тесты, которые при одном и том же коде могут то проходить успешно, то падать. В контексте CI/CD это катастрофа.

    Если пайплайн настроен строго (как и должно быть), падение хотя бы одного теста блокирует слияние кода (Merge). Если тест упал из-за того, что он Flaky, разработчик перезапускает пайплайн. Это подрывает доверие к автоматизации: команда начинает игнорировать красные пайплайны, думая, что «это опять тесты моргают, а не код сломан».

    В финтехе основные причины Flaky-тестов при параллельном запуске в CI/CD:

  • Конфликт тестовых данных: Два потока одновременно пытаются списать деньги с одного и того же тестового счета в базе данных.
  • Проблемы с сетью: Кратковременный таймаут при обращении к внешнему API (например, шлюзу Visa/Mastercard).
  • Неявные ожидания в UI: Тест пытается кликнуть на кнопку до того, как завершилась анимация загрузки страницы.
  • Как бороться с Flaky-тестами в CI

  • Изоляция данных: Каждый тест должен создавать уникального пользователя в БД (фаза Arrange) и удалять его после себя (фаза Teardown), как мы обсуждали в статье про проектирование фреймворка.
  • Механизм Retry (Перезапуск): В Gradle или JUnit можно настроить автоматический перезапуск упавшего теста. Если со второго раза он прошел — тест помечается как Flaky, но пайплайн не блокируется.
  • Карантин: Если тест стабильно «моргает», его помечают аннотацией @Disabled или специальным тегом @Quarantine. Он исключается из основного пайплайна до тех пор, пока QA-инженер его не починит.
  • > «Нестабильный автотест хуже, чем отсутствие автотеста. Отсутствие теста заставляет вас проверять функционал руками. Нестабильный тест заставляет вас игнорировать реальные ошибки». > > Блог HireHi

    Интеграция с Allure и уведомления

    Когда пайплайн завершается (успешно или с ошибкой), CI-сервер должен сформировать понятный отчет. Читать «сырые» логи Bash в консоли CI неудобно.

    Для этого используется Allure Framework. В нашем .gitlab-ci.yml мы указали сохранение папки build/allure-results/ как артефакта. Артефакты — это файлы, которые CI-сервер сохраняет после завершения работы раннера.

    Следующим шагом (stage: report) CI-сервер берет эти JSON-файлы, генерирует из них красивый HTML-отчет и публикует его на внутреннем сервере компании (Allure TestOps или GitHub Pages).

    Завершающий аккорд CI/CD для тестировщика — настройка уведомлений. С помощью простых Bash-скриптов и утилиты curl (которая отправляет HTTP-запросы), пайплайн может отправить сообщение в рабочий чат Telegram или Slack:

    Внедрение CI/CD превращает набор разрозненных скриптов на Kotlin в полноценный инженерный продукт. Автотесты становятся неотъемлемой частью конвейера поставки ценности, защищая финтех-продукт от регрессионных ошибок в режиме 24/7.

    2. Принципы автоматизации тестирования

    Принципы автоматизации тестирования

    Автоматизация тестирования — это процесс разработки программного обеспечения, единственная цель которого — проверять работу другого программного обеспечения. Для инженера по автоматизации (QA Automation Engineer) написание автотестов ничем не отличается от классической разработки: здесь применяются те же языки программирования (Java, Kotlin), те же архитектурные паттерны и те же правила чистого кода.

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

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

    Нельзя просто взять и автоматизировать все действия пользователя в браузере или мобильном приложении. Пользовательский интерфейс (UI) изменчив, элементы загружаются с разной скоростью, а браузеры могут вести себя непредсказуемо. Если вы попытаетесь покрыть 100% функционала тестами через UI, вы получите медленный, нестабильный и невероятно дорогой в поддержке проект.

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

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

    Пирамида состоит из трех базовых уровней:

    1. Модульные тесты (Unit Tests)

    Это фундамент пирамиды. Модульные тесты проверяют мельчайшие частицы кода — отдельные функции или методы классов в изоляции от остальной системы.

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

  • Кто пишет: Разработчики (иногда QA Automation).
  • Инструменты: JUnit (для Java/Kotlin), Mockito.
  • Скорость: Выполняются за миллисекунды.
  • 2. Интеграционные и API тесты (Integration / API Tests)

    Средний уровень пирамиды. Здесь проверяется, как различные модули системы работают вместе, а также тестируются интерфейсы программирования приложений (API). В современной микросервисной архитектуре это самый важный уровень для QA Automation Engineer.

    Вместо того чтобы нажимать кнопку «Перевести деньги» в интерфейсе, автотест отправляет прямой HTTP-запрос к серверу и проверяет, что баланс счетов в базе данных изменился корректно.

  • Кто пишет: QA Automation Engineer.
  • Инструменты: REST Assured, Retrofit, Spring Test.
  • Скорость: Выполняются за секунды.
  • 3. Сквозные тесты (End-to-End / UI Tests)

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

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

  • Кто пишет: QA Automation Engineer.
  • Инструменты: Selenium, Playwright, Appium (для мобильных приложений).
  • Скорость: Выполняются минутами.
  • > Представьте утро. Вы открываете ноутбук, заходите в Allure — и видите красное море. Падает половина автотестов, часть — «временно», часть — «иногда». Почти каждый день начинается с одних и тех же починок, дебага и «вроде теперь стабильно». > > Хабр

    Чтобы избежать описанной выше ситуации, необходимо смещать фокус проверок вниз по пирамиде — на уровень API.

    Анатомия автотеста: Паттерн AAA

    Любой грамотно написанный автотест, независимо от языка программирования, строится по паттерну AAA (Arrange, Act, Assert) — Подготовка, Действие, Проверка.

    Рассмотрим этот паттерн на примере тестирования API для создания банковского счета с использованием языка Kotlin.

    1. Arrange (Подготовка)

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

    2. Act (Действие)

    Выполнение целевого действия, которое мы хотим протестировать. Это вызов функции, отправка HTTP-запроса или клик по кнопке.

    3. Assert (Проверка)

    Сравнение фактического результата с ожидаемым. Если они совпадают — тест пройден (зеленый). Если нет — тест провален (красный).

    Пример автотеста на Kotlin с использованием библиотеки REST Assured:

    Этот код лаконичен и легко читается. Kotlin позволяет избавиться от лишнего шаблонного кода (boilerplate), который часто встречается в Java, делая тесты более понятными.

    Основы HTTP и REST для автоматизатора

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

    HTTP (HyperText Transfer Protocol) — это протокол передачи данных. Он работает по принципу «клиент-сервер». Клиент (мобильное приложение или ваш автотест) отправляет Запрос (Request), а сервер (банковская система) возвращает Ответ (Response).

    !Интерактивный симулятор HTTP-запросов

    HTTP Методы (Глаголы)

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

    | Метод | Описание | Пример в Финтехе | | :--- | :--- | :--- | | GET | Получить данные. Не изменяет состояние системы. | Запрос истории транзакций или текущего баланса. | | POST | Создать новый ресурс или выполнить операцию. | Отправка перевода, создание нового клиента. | | PUT | Полностью обновить существующий ресурс. | Обновление паспортных данных в профиле. | | PATCH | Частично обновить ресурс. | Изменение только номера телефона. | | DELETE | Удалить ресурс. | Закрытие банковского счета. |

    HTTP Статус-коды

    Каждый ответ сервера содержит трехзначный код, который сообщает о результате операции. В автотестах блок Assert почти всегда начинается с проверки статус-кода.

  • 2xx (Успех):
  • - 200 OK — запрос выполнен успешно. - 201 Created — ресурс успешно создан (например, выпущен новый токен).
  • 4xx (Ошибка клиента):
  • - 400 Bad Request — неверный запрос (например, попытка перевести отрицательную сумму). - 401 Unauthorized — клиент не авторизован (отсутствует или просрочен токен доступа). - 403 Forbidden — авторизация есть, но нет прав (попытка посмотреть чужой счет). - 404 Not Found — ресурс не найден (запрос к несуществующему счету).
  • 5xx (Ошибка сервера):
  • - 500 Internal Server Error — сервер упал из-за ошибки в коде. Для QA это сигнал о том, что найден критический баг.

    Роль баз данных и SQL в тестировании

    Автоматизация тестирования не ограничивается отправкой HTTP-запросов. В финтехе данные — это деньги. Если API вернуло статус 200 OK при переводе, это еще не значит, что деньги реально списались.

    QA Automation Engineer использует базы данных (PostgreSQL, Oracle) и язык SQL для двух целей:

  • Подготовка данных (Arrange): Перед запуском теста нужно создать пользователя с определенным балансом. Автотест может выполнить SQL-запрос INSERT INTO accounts (user_id, balance) VALUES (1, 5000);.
  • Проверка состояния (Assert): После выполнения перевода через API, автотест подключается к базе данных и выполняет SELECT balance FROM accounts WHERE user_id = 1;, чтобы убедиться, что баланс действительно уменьшился.
  • Фундаментальные правила стабильных автотестов

    Чтобы автотесты приносили пользу, а не головную боль, необходимо соблюдать строгие инженерные принципы.

    1. Атомарность и независимость

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

    Антипаттерн: Тест А создает пользователя, Тест Б авторизует этого пользователя, Тест В делает перевод. Если Тест А упадет из-за сетевой ошибки, упадут и Б, и В, хотя функционал перевода может работать идеально.

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

    2. Изоляция тестовых данных

    Тесты не должны использовать общие данные. Если два параллельно запущенных теста попытаются списать деньги с одного и того же тестового счета, возникнет состояние гонки (Race Condition), и один из тестов упадет. Каждый тест должен генерировать уникальные данные (например, случайные номера телефонов или email-адреса) для своего прогона.

    3. Отказ от жестких ожиданий (Sleep)

    В коде автотестов категорически запрещено использовать команды вроде Thread.sleep(5000) (подождать 5 секунд). Сервер может ответить за 1 секунду, и тогда тест впустую потеряет 4 секунды. А если сервер ответит за 6 секунд — тест упадет.

    Вместо этого используются явные ожидания (Explicit Waits) или библиотеки вроде Awaitility. Тест постоянно опрашивает систему: «Готово? Нет. Готово? Нет. Готово? Да!» — и мгновенно продолжает работу, как только условие выполнено.

    4. Принцип единой ответственности (SRP)

    Не пишите огромные E2E-тесты, которые проверяют всё подряд. Если цель — убедиться, что при регистрации пользователь получает SMS с кодом, не нужно писать тест, который открывает браузер, заполняет 10 полей, ждет SMS, вводит его и проверяет профиль.

    Разбейте это на атомарные проверки: один тест проверяет API регистрации, другой проверяет интеграцию с сервисом отправки SMS. Это делает тесты быстрыми и точно указывает на место поломки.

    Инфраструктура автоматизации: Git, Bash и Allure

    Автотесты не живут в вакууме на компьютере тестировщика. Они являются частью процесса непрерывной интеграции (CI/CD). Для управления этим процессом QA-инженер должен владеть инфраструктурными инструментами.

    Система контроля версий (Git) Код автотестов хранится в репозиториях (например, GitHub или GitLab) точно так же, как и код самого приложения. Git позволяет командам работать над тестами совместно, отслеживать историю изменений и откатывать код назад, если новая версия тестов оказалась нерабочей.

    Командная строка (Bash) Запуск тестов на удаленных серверах происходит без графического интерфейса. QA-инженер должен уметь использовать терминал Linux (Bash), чтобы склонировать репозиторий, запустить сборку проекта (например, через Gradle или Maven) и прочитать логи сервера, если тест упал.

    Системы отчетности (Allure) Бизнесу и разработчикам не нужен исходный код ваших тестов. Им нужен понятный ответ на вопрос: «Можно ли выпускать продукт в релиз?». Инструменты вроде Allure генерируют красивые HTML-отчеты на основе прогона тестов. В отчете видно, сколько тестов прошло, сколько упало, сколько времени это заняло. К упавшим тестам Allure автоматически прикрепляет логи HTTP-запросов, ответы базы данных и скриншоты экранов.

    Освоив эти принципы и инструменты, вы перестанете быть просто «тестировщиком, который умеет программировать», и станете настоящим QA Automation Engineer, способным выстраивать надежные процессы контроля качества в сложных финтех-проектах.

    20. Контейнеризация автотестов с использованием Docker

    Контейнеризация автотестов с использованием Docker

    В предыдущей статье мы рассматривали настройку процессов непрерывной интеграции (CI/CD). Мы выяснили, что пайплайн запускает тесты на удаленных серверах — раннерах, выполняя команды через командную строку Bash. Однако здесь возникает критическая проблема, с которой сталкивается каждый QA Automation Engineer: зависимость от окружения.

    Представьте ситуацию: вы написали идеальный фреймворк на Kotlin, использующий возможности Java 17. Вы запускаете тесты локально — всё работает. Вы отправляете код в Git, пайплайн запускается на раннере, и тесты падают с ошибкой UnsupportedClassVersionError. Причина банальна: системный администратор установил на раннер Java 11. Или, например, ваши UI-тесты на Playwright требуют определенной версии браузера Chrome, которой нет на сервере.

    Проблема «на моем компьютере всё работает» — одна из самых старых в IT. Для её решения была создана технология контейнеризации, а индустриальным стандартом в этой области стал Docker.

    Концепция контейнеризации

    Исторически для изоляции приложений использовались виртуальные машины (Virtual Machines, VM). Виртуальная машина эмулирует аппаратное обеспечение (процессор, память, жесткий диск) и требует установки полноценной гостевой операционной системы (например, Ubuntu или Windows) поверх вашей основной ОС.

    Это надежно, но крайне ресурсозатратно. Если вам нужно запустить три разных тестовых окружения, вам придется поднять три виртуальные машины, каждая из которых будет потреблять гигабайты оперативной памяти только на поддержание работы своей ОС.

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

    Для виртуальных машин:

    Для контейнеров Docker:

    Где — память, потребляемая операционной системой, а — память, необходимая самому приложению. В случае с Docker операционная система (ядро) загружается только один раз, а контейнеры делят её ресурсы, оставаясь при этом изолированными друг от друга.

    !Архитектура виртуальных машин в сравнении с контейнерами Docker

    Docker упаковывает ваше приложение (в нашем случае — автотесты на Kotlin) вместе со всеми его зависимостями (Java JDK, Gradle, браузеры, системные библиотеки) в единый стандартизированный блок — контейнер. Этот контейнер будет работать абсолютно одинаково на вашем ноутбуке с macOS, на сервере с Linux или в облаке AWS.

    Сравнение подходов

    | Характеристика | Виртуальная машина (VM) | Docker-контейнер | | :--- | :--- | :--- | | Изоляция | Полная (на уровне железа) | Частичная (на уровне процессов ОС) | | Время запуска | Минуты (загрузка ОС) | Секунды или миллисекунды | | Размер | Гигабайты | Мегабайты | | Утилизация ресурсов | Высокая (ОС забирает ресурсы) | Низкая (ресурсы идут только на приложение) |

    Святая троица Docker: Dockerfile, Image, Container

    Чтобы начать работать с Docker, необходимо четко понимать разницу между тремя фундаментальными сущностями.

  • Dockerfile — это текстовый файл с инструкциями. Это рецепт, в котором написано: «Возьми Linux, установи туда Java 17, скопируй исходный код моих тестов и выполни команду сборки».
  • Image (Образ) — это результат выполнения Dockerfile. Это неизменяемый (read-only) слепок файловой системы. Если провести аналогию с объектно-ориентированным программированием на Kotlin, то Image — это класс.
  • Container (Контейнер) — это запущенный экземпляр образа. В нем выполняются процессы, он имеет свою сеть и может изменять данные внутри себя. В терминах ООП Container — это объект (экземпляр класса).
  • > Docker — это инструмент, который позволяет создавать, разворачивать и запускать приложения с учетом всех зависимостей и среды выполнения. Как только вы настроите виртуальную машину, вы сможете передать её любому человеку с установленным Docker, и все будет работать как нужно. > > Docker для QA-инженеров

    Написание Dockerfile для Kotlin-фреймворка

    Давайте создадим Dockerfile для нашего финтех-фреймворка автотестов. Файл создается в корне проекта (там же, где лежит build.gradle.kts) и не имеет расширения.

    Оптимизация слоев (Кэширование)

    Обратите внимание на порядок команд COPY. Почему мы сначала копируем build.gradle.kts, затем запускаем скачивание зависимостей (RUN gradle dependencies), и только потом копируем папку src с кодом тестов?

    Docker собирает образы послойно. Каждая инструкция в Dockerfile создает новый слой. Если слой не изменился с прошлой сборки, Docker берет его из кэша, что экономит минуты времени.

    Код тестов (папка src) меняется постоянно — вы пишете новые проверки каждый день. А вот список библиотек (файл build.gradle.kts) меняется редко. Разделив эти шаги, мы гарантируем, что при изменении кода тестов Docker не будет заново скачивать сотни мегабайт библиотек из интернета. Он возьмет слой с зависимостями из кэша и обновит только слой с исходным кодом.

    !Интерактивная визуализация сборки Docker-образа по слоям

    Сборка и запуск через Bash

    Имея Dockerfile, мы можем собрать образ и запустить контейнер, используя командную строку Bash.

    Сборка образа (флаг -t задает имя и тег):

    Запуск контейнера:

    Контейнер запустится, выполнит команду gradle test, прогонит автотесты и остановится. Но здесь возникает проблема: как получить отчет Allure? Контейнер изолирован, и когда он завершает работу, все сгенерированные внутри него файлы (включая папку build/allure-results) остаются запертыми внутри.

    Работа с Volumes (Томами)

    Чтобы вытащить данные из контейнера на хост-машину (ваш ноутбук или CI-сервер), используются Volumes (Тома). Это механизм проброса директории из вашей реальной ОС внутрь файловой системы контейнера.

    Изменим команду запуска: yaml version: '3.8'

    services: # Сервис 1: База данных PostgreSQL db: image: postgres:15-alpine environment: POSTGRES_USER: qa_user POSTGRES_PASSWORD: qa_password POSTGRES_DB: fintech_db ports: - "5432:5432"

    # Сервис 2: Наши автотесты tests: build: . # Собираем образ из Dockerfile в текущей папке depends_on: - db # Тесты не начнут запуск, пока не поднимется БД environment: - DB_HOST=db # Имя сервиса становится DNS-именем в сети Docker - DB_USER=qa_user - DB_PASSWORD=qa_password volumes: - ./allure-results:/app/build/allure-results bash docker-compose up --build --abort-on-container-exit ``

    Флаг --abort-on-container-exit критически важен для CI/CD: он указывает Docker Compose остановить все контейнеры (включая базу данных), как только контейнер с тестами завершит свою работу. Это гарантирует, что после прогона тестов сервер останется чистым.

    Интеграция с CI/CD

    Возвращаясь к нашему пайплайну из предыдущей статьи, внедрение Docker кардинально меняет подход. Теперь раннеру (GitLab CI, Jenkins) не нужно иметь установленную Java, Gradle или браузеры.

    Единственное требование к раннеру — на нем должен быть установлен сам Docker. Пайплайн просто выполняет команду docker-compose up`, и вся необходимая среда (БД, тесты, мок-серверы) поднимается из контейнеров, отрабатывает и бесследно исчезает.

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

    3. Работа с командной строкой Bash

    Работа с командной строкой Bash

    В предыдущих материалах мы разобрали архитектуру автотестов, пирамиду тестирования и принципы работы с HTTP-запросами. Вы научились отправлять запросы к API и проверять ответы. Но что происходит, когда ваш идеально написанный на Kotlin автотест падает, потому что сервер вернул статус 500 Internal Server Error?

    В финтех-компаниях серверы, обрабатывающие транзакции, переводы и кредитные скоринги, работают под управлением операционных систем семейства Linux. На этих серверах нет графического интерфейса (GUI), мышки или привычных окон. Единственный способ узнать, почему упал сервер, найти ошибку в логах или перезапустить зависший процесс — это использовать интерфейс командной строки (CLI) и оболочку Bash.

    Для QA Automation Engineer владение командной строкой — это граница, отделяющая начинающего специалиста от инженера уровня Middle.

    Что такое Bash и зачем он автоматизатору

    Bash (Bourne Again Shell) — это командная оболочка, программа, которая принимает ваши текстовые команды, передает их операционной системе для выполнения и возвращает результат на экран.

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

  • Подключиться к удаленному тестовому серверу.
  • Найти папку, куда приложение пишет журналы событий (логи).
  • Отфильтровать файл размером в несколько гигабайт, чтобы найти конкретный transaction_id.
  • Понять причину ошибки и приложить выдержку из лога к баг-репорту.
  • Все эти действия выполняются исключительно через терминал.

    Навигация и работа с файлами

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

    Где я нахожусь? (pwd и ls)

    Когда вы открываете терминал, вы попадаете в определенную директорию. Чтобы узнать свой текущий путь, используется команда pwd (Print Working Directory).

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

    QA-инженеру часто нужно создавать тестовые файлы, копировать конфигурации или удалять старые отчеты.

  • Создание пустого файла: touch test-data.json
  • Создание новой папки: mkdir new-reports (Make Directory)
  • Копирование: cp config.yml config-backup.yml (Copy)
  • Перемещение или переименование: mv old-name.txt new-name.txt (Move)
  • Удаление: rm error.log (Remove). Для удаления папки со всем содержимым используется rm -rf folder_name (применять с крайней осторожностью!).
  • Чтение и анализ логов: Главный навык QA

    Финтех-приложения генерируют огромные объемы логов. Каждая авторизация, каждый перевод средств записывается в текстовые файлы. Открывать файл размером 5 ГБ в обычном текстовом редакторе бессмысленно — компьютер просто зависнет. Bash предлагает специализированные инструменты.

    Просмотр файлов (cat и less)

    Команда cat (Concatenate) выводит всё содержимое файла в терминал разом. Это удобно для маленьких файлов, например, конфигураций.

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

    Практический сценарий: Вы запускаете автотест на оплату картой со своего ноутбука, а на соседнем мониторе в терминале сервера смотрите, как бегут логи. Как только тест падает, вы сразу видите ошибку (например, NullPointerException) в реальном времени.

    Поиск по тексту (grep)

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

    Предположим, у вас есть лог за весь день, и вам нужно найти информацию по конкретному клиенту с ID 778899.

    Символ >> делает то же самое, но добавляет текст в конец файла, не удаляя старое содержимое.

    Конвейеры (Pipes): Магия Bash

    > Философия Unix гласит: пишите программы, которые делают только одну вещь, но делают её хорошо. Пишите программы так, чтобы они могли работать вместе. > > Дуглас Макилрой, создатель концепции конвейеров

    Символ вертикальной черты | называется pipe (труба или конвейер). Он берет стандартный вывод (stdout) первой команды и передает его как стандартный ввод (stdin) второй команде.

    Это позволяет строить цепочки обработки данных невероятной мощности.

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

    Вместо того чтобы писать сложный скрипт на Kotlin, вы делаете это одной строкой в Bash:

    Второе число в строке (14582) — это PID (Process ID), уникальный идентификатор процесса.

    Чтобы принудительно завершить зависший процесс, используется команда kill с флагом -9 (сигнал безусловного завершения):

    После ввода пароля (или использования SSH-ключа) ваш терминал начинает управлять удаленным сервером так, будто вы сидите прямо перед ним.

    Если вам нужно скопировать файл с вашего компьютера на сервер (например, новый скомпилированный .jar файл с автотестами) или забрать логи с сервера на свой компьютер, используется команда SCP (Secure Copy), которая работает поверх SSH.

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

    Bash-скрипты vs Kotlin Scripting (KTS)

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

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

    Поскольку вы изучаете автоматизацию на Kotlin, важно знать о Kotlin Scripting (KTS). KTS позволяет писать исполняемые скрипты прямо на языке Kotlin. Вы можете использовать всю мощь строгой типизации, библиотек Java/Kotlin и привычный синтаксис для автоматизации серверных задач, заменяя сложные Bash-скрипты.

    Например, система сборки Gradle, которая повсеместно используется в Kotlin-проектах, использует именно KTS (build.gradle.kts) для описания процесса компиляции и запуска автотестов.

    Тем не менее, базовые команды Bash (cd, ls, grep, tail) остаются незаменимыми для быстрой интерактивной работы и диагностики проблем на серверах. Умение быстро найти ошибку в логах с помощью grep сэкономит вам часы отладки и сделает вас ценным специалистом в любой финтех-команде.

    4. Система контроля версий Git

    Система контроля версий Git

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

    Представьте ситуацию: вы написали отличный автотест на Kotlin, который проверяет логику начисления кэшбека по кредитной карте. Тест работает идеально. На следующий день ваш коллега решает добавить в этот же файл проверку для дебетовых карт, случайно удаляет часть вашего кода и сохраняет файл. Ваш тест сломан, а оригинальный код утерян навсегда.

    Чтобы избежать таких катастроф, управлять изменениями и работать в команде, используется система контроля версий (Version Control System, VCS). Безоговорочным стандартом в индустрии является Git.

    Что такое Git и зачем он автоматизатору

    Git — это распределенная система контроля версий. Это программа, которая отслеживает историю изменений в текстовых файлах (в нашем случае — в файлах с кодом на Kotlin, конфигурациях и скриптах Bash), позволяет возвращаться к предыдущим состояниям и объединять работу нескольких людей.

    Для QA Automation Engineer владение Git решает три критические задачи:

  • Сохранность кода: Каждое изменение фиксируется. Вы всегда можете откатиться к версии тестов, которая работала неделю назад.
  • Командная работа: Десятки автоматизаторов могут одновременно писать тесты для разных микросервисов банка, не мешая друг другу.
  • Интеграция с CI/CD: Системы непрерывной интеграции (например, Jenkins или GitLab CI) берут код ваших автотестов именно из Git, чтобы запустить их на сервере после каждого обновления приложения.
  • > Важно не путать Git и GitHub. Git — это консольная программа, установленная на вашем компьютере. GitHub (как и GitLab или Bitbucket) — это веб-сервис, облачное хранилище, где размещаются репозитории Git для совместного доступа.

    Три состояния файлов в Git

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

  • Рабочий каталог (Working Directory) — это папка на вашем компьютере, где лежат файлы проекта. Здесь вы пишете код в IntelliJ IDEA, создаете новые файлы и удаляете старые.
  • Индекс (Staging Area) — это зона подготовки. Сюда вы добавляете измененные файлы, которые планируете сохранить в следующем снимке состояния.
  • Репозиторий (Repository / .git directory) — скрытая база данных Git, где хранятся все сохраненные снимки состояния (коммиты) и история проекта.
  • !Схема трех состояний Git: Рабочий каталог, Индекс и Репозиторий

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

  • Вещи, разбросанные по комнате — это Рабочий каталог.
  • Вещи, которые вы положили в коробку, но еще не заклеили скотчем — это Индекс (вы еще можете что-то выложить или доложить).
  • Заклеенная коробка, переданная на почту с трек-номером — это Репозиторий (изменение зафиксировано навсегда).
  • Базовый локальный рабочий процесс

    Рассмотрим ежедневный цикл работы QA-инженера с Git через командную строку (Bash), которую мы изучили ранее.

    1. Инициализация (git init)

    Чтобы Git начал отслеживать изменения в папке, ее нужно превратить в репозиторий.

    Эта команда создает скрытую папку .git. С этого момента Git следит за всеми файлами в директории fintech-tests.

    2. Проверка статуса (git status)

    Самая частая команда. Она показывает, в каком состоянии находятся файлы.

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

    Флаг -m (message) позволяет передать сообщение коммита прямо в командной строке.

    Правило хорошего тона: Сообщение коммита должно отвечать на вопрос «Что делает этот коммит?», а не «Что я сделал?». Плохо: "исправил баг". Хорошо: "Исправлен таймаут ожидания ответа от платежного шлюза".

    Работа с удаленными репозиториями

    Хранить код только на своем ноутбуке небезопасно. В коммерческой разработке код всегда синхронизируется с удаленным сервером (Remote).

    Клонирование (git clone)

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

    Здесь origin — это стандартное имя удаленного сервера, а main — имя главной ветки проекта.

    Получение обновлений (git pull)

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

    Теперь вы находитесь в изолированной среде. Вы можете создавать файлы, делать коммиты, ломать код — это никак не повлияет на ветку main.

    !Интерактивная визуализация ветвления в Git

    Слияние веток (git merge)

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

    Для этого вы сначала переключаетесь обратно в главную ветку, а затем «втягиваете» в нее свои изменения:

    Конфликты слияния (Merge Conflicts)

    Слияние не всегда проходит гладко. Конфликт возникает, когда два человека изменили одну и ту же строку в одном и том же файле, и Git не знает, чью версию сохранить.

    Сценарий из финтеха: В файле Config.kt указан URL тестового сервера: val serverUrl = "https://test1.bank.com"

    Вы в своей ветке изменили его на test2.bank.com. Ваш коллега в своей ветке изменил его на dev.bank.com. При попытке слияния Git остановит процесс и выдаст сообщение о конфликте.

    Если вы откроете файл Config.kt, вы увидите специальные маркеры, добавленные Git:

    Разрешение конфликта — это ручной процесс. Вы должны удалить маркеры <<<<<<<, =======, >>>>>>> и оставить тот код, который считаете правильным. После этого файл нужно сохранить, добавить в индекс (git add) и завершить слияние новым коммитом.

    Игнорирование файлов (.gitignore)

    В любом проекте есть файлы, которые категорически нельзя добавлять в Git. Для проектов на Kotlin это:

  • Скомпилированные файлы (папки build/ или out/).
  • Настройки вашей локальной среды разработки (папка .idea/).
  • Сгенерированные отчеты о тестировании (папка allure-results/).
  • Файлы с паролями и секретными ключами от баз данных.
  • Если эти файлы попадут в репозиторий, они будут занимать лишнее место, вызывать постоянные конфликты и могут привести к утечке данных.

    Чтобы Git не замечал эти файлы, в корне проекта создается текстовый файл с именем .gitignore. В нем прописываются правила исключения.

    Пример .gitignore для проекта автотестов:

    Как только вы добавите папку в .gitignore, команда git status перестанет ее видеть, и вы случайно не отправите мусор на сервер.

    Резюме лучших практик для QA Automation

  • Делайте коммиты атомарными. Один коммит — одна логическая задача. Не смешивайте в одном коммите добавление нового теста и исправление старого бага.
  • Пишите понятные сообщения. Коллега должен по сообщению коммита понять, зачем было сделано изменение, не открывая сам код.
  • Используйте ветки. Никогда не пишите новый код прямо в main. Создавайте ветку под каждую задачу (например, bugfix/login-error или test/api-transfers).
  • Синхронизируйтесь чаще. Регулярно делайте git pull, чтобы ваш локальный код не сильно отставал от кода команды. Чем дольше вы не обновляетесь, тем больнее будет разрешать конфликты слияния.
  • Владение Git через командную строку дает глубокое понимание процессов. В реальной работе вы часто будете использовать визуальные интерфейсы, встроенные в IntelliJ IDEA, но при возникновении сложных конфликтов или сбоев именно знание базовых консольных команд Git спасет ваш проект.

    5. Паттерны проектирования в автоматизации тестирования

    Паттерны проектирования в автоматизации тестирования

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

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

    Чтобы избежать подобных ситуаций, инженеры используют паттерны проектирования (Design Patterns) — проверенные временем типовые решения часто встречающихся архитектурных проблем. В этой статье мы разберем ключевые паттерны, которые должен знать QA Automation Engineer, и посмотрим, как элегантно они реализуются на языке Kotlin.

    Что такое паттерны и зачем они нужны

    Паттерн — это не готовая библиотека, которую можно скачать. Это концепция, схема организации кода. Использование паттернов в автоматизации тестирования решает три главные задачи:

  • Снижение стоимости поддержки (Maintenance Cost): Изменения в продукте требуют минимальных изменений в коде тестов.
  • Повышение читаемости: Тесты начинают выглядеть как бизнес-требования, понятные даже ручным тестировщикам или аналитикам.
  • Устранение дублирования (DRY - Don't Repeat Yourself): Общая логика выносится в переиспользуемые компоненты.
  • Математика поддержки тестов очень проста. Если — это время на обновление одного элемента (например, локатора кнопки), а — количество тестов, использующих этот элемент, то общее время на починку тестов без паттернов составит . При правильном использовании паттернов формула превращается в , так как элемент обновляется ровно в одном месте.

    1. Page Object Model (POM)

    Page Object Model — это абсолютный стандарт де-факто в автоматизации UI-тестирования (как веб, так и мобильных приложений).

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

    Пример из финтеха: У нас есть страница авторизации в интернет-банке.

    Без паттерна POM тест выглядит как мешанина из технических деталей и бизнес-логики:

    Если ID поля ввода пароля изменится, этот тест сломается. А если таких тестов 50?

    Применим паттерн POM. Сначала создадим класс страницы:

    Теперь сам тест становится невероятно чистым и читаемым:

    Если разработчики изменят локатор кнопки входа, вы обновите его только в одной строке внутри класса LoginPage. Все 50 тестов, использующих этот класс, починятся автоматически.

    !Схема архитектуры Page Object Model

    2. Singleton (Одиночка)

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

    В автоматизации тестирования этот паттерн критически важен для управления «тяжелыми» ресурсами. Например, подключение к базе данных PostgreSQL для проверки транзакций занимает время. Если каждый из 200 тестов будет открывать свое собственное подключение, база данных может не выдержать нагрузки, а тесты будут идти очень долго.

    В классической Java реализация Singleton требует написания приватных конструкторов, статических переменных и синхронизации потоков. В Kotlin этот паттерн встроен в язык на уровне синтаксиса через ключевое слово object.

    Использование в тестах:

    > Важное правило: используйте Singleton только для stateless компонентов (компонентов без состояния), таких как клиенты БД или HTTP-клиенты. Если Singleton хранит данные конкретного теста, параллельный запуск тестов приведет к непредсказуемым ошибкам (состояние гонки).

    3. Builder (Строитель) и Data Classes

    Паттерн Builder используется для пошагового создания сложных объектов. В контексте QA Automation он незаменим при тестировании API, когда нужно генерировать большие JSON-тела запросов (payloads).

    Пример из финтеха: API для создания заявки на кредит требует передать объект с десятками полей (имя, паспорт, доход, поручители и т.д.). В 90% тестов нам важны только 2-3 поля, а остальные могут быть заполнены значениями по умолчанию.

    В Java для этого пришлось бы писать громоздкий класс-строитель. Kotlin предлагает более элегантное решение — Data Classes (классы данных) с параметрами по умолчанию.

    Теперь в тестах мы можем создавать объекты, переопределяя только те поля, которые важны для конкретного сценария. Это и есть современная Kotlin-альтернатива паттерну Builder:

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

    Такой подход делает подготовку тестовых данных (этап Arrange из паттерна AAA) быстрой, безопасной и невероятно читаемой.

    4. Facade (Фасад) / Steps

    Паттерн Facade скрывает сложную систему классов за простым интерфейсом. В автоматизации тестирования этот паттерн часто называют Steps (Шаги).

    Представьте E2E (End-to-End) тест, который проверяет перевод денег между двумя новыми пользователями. Чтобы тест дошел до самого перевода, нужно выполнить огромную подготовительную работу через API:

  • Создать Пользователя А.
  • Пройти KYC (верификацию документов) для Пользователя А.
  • Пополнить счет Пользователя А.
  • Создать Пользователя Б.
  • Если писать все эти HTTP-запросы прямо в тесте, он растянется на 200 строк. Паттерн Facade предлагает вынести эту логику в отдельный класс-помощник.

    Теперь наш сложный E2E тест выглядит как поэма:

    !Интерактивная симуляция поддержки тестов с паттернами и без

    Антипаттерны: Как делать не надо

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

    | Антипаттерн | Описание | Почему это плохо | Правильное решение | | :--- | :--- | :--- | :--- | | Hardcoded Data | Жестко заданные тестовые данные (например, val email = "test@mail.ru") прямо в коде теста. | Если тест запустить дважды, второй раз он упадет, так как email уже занят базой данных. | Использовать генераторы случайных данных (Faker) или паттерн Builder. | | God Object | Создание одного огромного класса BaseTest, в который складываются вообще все вспомогательные методы проекта. | Класс разрастается до тысяч строк, в нем невозможно ориентироваться, возникают конфликты при слиянии в Git. | Разделять логику по доменам (например, AuthSteps, PaymentSteps, ProfileSteps). | | Chained Tests | Тест Б зависит от результатов Теста А. (Например, Тест А создает юзера, а Тест Б его удаляет). | Если Тест А упадет из-за сетевой ошибки, Тест Б тоже упадет, хотя функционал удаления работает отлично. | Каждый тест должен быть атомарным. Он должен сам готовить для себя данные и сам за собой убирать. |

    Резюме

    Паттерны проектирования — это язык, на котором общаются профессиональные инженеры.

    Используя Page Object Model, вы защищаете UI-тесты от постоянных поломок при изменении верстки. Применяя Singleton (через Kotlin object), вы экономите ресурсы системы при работе с БД. Используя Data Classes вместо классического Builder, вы элегантно генерируете JSON-тела для API-запросов. А оборачивая сложные последовательности действий в Facade (Steps), вы делаете тесты читаемыми для всей команды.

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

    6. Продвинутый Kotlin для автотестов

    Продвинутый Kotlin для автотестов

    В предыдущей статье мы разобрали паттерны проектирования и выяснили, как Page Object Model и Data Classes спасают проекты от хаоса. Вы увидели, что правильная архитектура снижает стоимость поддержки тестов. Однако архитектура — это лишь каркас. То, насколько изящно и надежно будет реализован этот каркас, зависит от знания инструментов самого языка программирования.

    Язык Kotlin создавался как современная, лаконичная и безопасная альтернатива Java. В контексте автоматизации тестирования финтех-приложений, где цена ошибки — это реальные деньги пользователей, возможности Kotlin позволяют писать тесты, которые читаются как бизнес-требования и работают максимально стабильно.

    Разберем продвинутые конструкции Kotlin, которые отличают начинающего автоматизатора от Senior QA Automation Engineer.

    1. Функции-расширения (Extension Functions)

    Представьте, что вы тестируете банковское API. Сервер возвращает номер кредитной карты в виде строки "1234567812345678". В логах отчетов Allure и в самих проверках по правилам безопасности (PCI DSS) вы обязаны маскировать этот номер, оставляя только последние 4 цифры: "* * 5678".

    В классической Java для этого пришлось бы создавать класс-утилиту StringUtils, писать статический метод maskCardNumber(String card) и вызывать его повсюду. Код становится процедурным и трудночитаемым.

    Kotlin предлагает функции-расширения (Extension Functions). Этот механизм позволяет добавлять новые методы к уже существующим классам (даже к тем, исходный код которых вы не можете изменить, например, к стандартному классу String), не прибегая к наследованию.

    !Схема работы Scope-функций в Kotlin

    3. Sealed Classes: Строгий контроль состояний

    В финтех-приложениях сущности часто имеют строгий набор состояний. Транзакция может быть в статусе PENDING (в обработке), SUCCESS (успешно) или DECLINED (отклонена).

    Обычно для этого используют перечисления (enum). Но enum имеет ограничение: все его элементы — это константы, они не могут хранить уникальные данные для каждого состояния. Например, если транзакция отклонена, нам нужна причина (reason), а если успешна — номер квитанции (receiptId).

    Здесь на помощь приходят Sealed Classes (изолированные классы). Это продвинутая версия enum. Sealed класс ограничивает иерархию наследования: все классы-наследники должны быть объявлены в том же файле. При этом каждый наследник может быть полноценным классом со своими уникальными свойствами.

    Главная суперсила Sealed классов раскрывается при использовании оператора when (аналог switch в Java). Компилятор Kotlin знает всех возможных наследников Sealed класса. Если вы забудете обработать хотя бы одно состояние в тесте, код просто не скомпилируется.

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

    4. Корутины (Coroutines): Асинхронность без боли

    Автоматизация тестирования — это постоянное ожидание. Мы ждем, пока загрузится веб-страница, пока база данных обновит запись, пока микросервис обработает сообщение из брокера Kafka.

    Использование Thread.sleep(5000) — это главный антипаттерн. Он блокирует текущий поток операционной системы намертво. Если у вас 100 тестов, и каждый ждет по 5 секунд, вы теряете 500 секунд (более 8 минут) впустую.

    Математика параллельного выполнения проста. Если — время выполнения одного теста, а — количество тестов, то при последовательном запуске общее время . При идеальном параллельном запуске время равно самому долгому тесту: . Но чтобы запускать тесты параллельно, нужны свободные потоки. Если потоки заблокированы Thread.sleep, параллельность рушится.

    Kotlin решает эту проблему с помощью Корутин (Coroutines) — легковесных потоков. Корутины позволяют приостановить выполнение кода (suspend), освободив системный поток для других задач, а затем возобновить работу.

    Вместо блокирующего Thread.sleep() в корутинах используется delay().

    Пример из финтеха: Асинхронный поллинг (опрос) базы данных. Мы перевели деньги и ждем, пока статус в БД изменится на SUCCESS.

    Использование корутин в связке с современными тестовыми фреймворками (например, Kotest) позволяет запускать тысячи API-тестов одновременно на обычном ноутбуке, так как они не потребляют тяжелые потоки операционной системы.

    !Интерактивная визуализация: Потоки (Threads) против Корутин (Coroutines)

    5. Построение DSL (Domain-Specific Language)

    Вершина мастерства в Kotlin — это создание собственных DSL (предметно-ориентированных языков). Комбинируя функции-расширения, лямбда-выражения и Scope-функции, вы можете создать синтаксис, который будет выглядеть как конфигурационный файл или текст на английском языке.

    Зачем это нужно? Чтобы тесты могли читать и проверять системные аналитики или ручные тестировщики, не знающие программирования.

    Сравните стандартный подход к созданию пользователя:

    И подход с использованием Kotlin DSL:

    Второй вариант выглядит как JSON. Чтобы реализовать такой синтаксис, инженеры пишут специальные функции-строители (Builders), которые принимают лямбды с контекстом (receiver).

    В финтех-тестировании DSL часто используют для генерации сложных моков (заглушек) ответов от сторонних систем (например, от платежных шлюзов Visa/Mastercard) или для описания шагов сложного E2E сценария.

    Интеграция с инфраструктурой

    Важно понимать, что красивый код на Kotlin — это лишь часть работы QA Automation Engineer. Написанные тесты должны запускаться в CI/CD (например, GitLab CI или Jenkins).

    Для этого ваш Kotlin-код компилируется и запускается через командную строку с помощью систем сборки (Gradle или Maven). Именно здесь вам пригодятся знания Bash, о которых мы говорили ранее. Вы будете писать Bash-скрипты, которые скачивают исходный код через Git, запускают команду ./gradlew test, а затем генерируют красивый HTML-отчет с помощью Allure.

    Современный фреймворк для тестирования на Kotlin (например, JUnit 5 или Kotest) имеет встроенные интеграции с Allure. Достаточно добавить аннотацию @Step к вашим функциям-расширениям или методам внутри DSL, и каждое действие (например, toMaskedCard()) будет автоматически задокументировано в итоговом отчете с указанием времени выполнения и переданных параметров.

    Освоив функции-расширения, Scope-функции, Sealed классы и корутины, вы перестаете писать тесты в стиле «Java на минималках» и начинаете использовать всю мощь идиоматичного Kotlin. Это позволяет сократить объем кода на 30-40%, сделать его устойчивым к изменениям и понятным для всей команды.

    7. Библиотеки автотестирования на Kotlin

    Экосистема и библиотеки: Инструментарий QA Automation Engineer

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

    В финтех-отрасли требования к инструментам особенно высоки. Библиотеки должны обеспечивать высокую скорость выполнения, абсолютную стабильность (отсутствие flaky tests — мигающих тестов) и формировать прозрачную отчетность, которую смогут читать аудиторы и бизнес-аналитики.

    Разберем ключевые слои автоматизации тестирования и современные библиотеки на базе Kotlin, которые стали стандартом индустрии.

    Движки выполнения тестов (Test Runners)

    Test Runner (тестовый движок) — это сердце любого фреймворка автоматизации. Это программа, которая сканирует ваш код, находит функции, помеченные как тесты, запускает их в определенном порядке, перехватывает ошибки и собирает базовую статистику (упал/прошел).

    JUnit 5: Индустриальный стандарт

    Исторически в мире Java доминировал JUnit. Его современная версия, JUnit 5, состоит из трех модулей: Platform (базовый API для запуска), Jupiter (модель программирования и аннотации) и Vintage (поддержка старых тестов).

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

    Преимущество JUnit 5 — колоссальная база знаний и нативная интеграция с любой системой сборки (Gradle, Maven) и средой разработки (IntelliJ IDEA).

    Kotest: Нативный подход Kotlin

    Несмотря на мощь JUnit 5, он остается Java-фреймворком. Для тех, кто хочет использовать все возможности Kotlin (например, DSL и корутины), был создан Kotest.

    Kotest предлагает несколько стилей написания тестов. В финтехе популярен BehaviorSpec, который реализует подход BDD (Behavior-Driven Development). Тесты читаются как спецификация требований.

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

    Тестирование REST API

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

    REST Assured: Проверенная классика

    REST Assured — это Java-библиотека, созданная специально для тестирования REST API. Она использует текучий интерфейс (Fluent API) в стиле Given-When-Then.

    Пример авторизации и проверки профиля пользователя:

    REST Assured отлично справляется с базовыми задачами, но имеет недостатки при работе с Kotlin: необходимость экранировать слово ` when (так как в Kotlin это зарезервированный оператор) и слабая типизация при проверке JSON-ответов.

    Retrofit + OkHttp: Строгая типизация

    Альтернативный подход — использовать библиотеки, которые применяют сами Android-разработчики. Retrofit превращает HTTP API в интерфейс Kotlin.

    Сначала мы описываем API с помощью аннотаций и Data Classes:

    Затем используем этот интерфейс в тесте:

    Этот подход гарантирует типобезопасность (Type Safety). Если разработчики изменят структуру JSON, ваш код просто не скомпилируется, что предотвратит падение тестов в рантайме.

    !Схема архитектуры тестового фреймворка: от Test Runner к слоям API, UI и БД, завершаясь генерацией отчета Allure

    Автоматизация Web UI

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

    Selenide: Умная обертка над Selenium

    Долгие годы стандартом был Selenium WebDriver. Однако писать на чистом Selenium сложно: элементы загружаются асинхронно, и тесты часто падают из-за того, что кнопка еще не появилась на экране. Инженерам приходилось писать сложные конструкции ожидания (WebDriverWait).

    Selenide решает эту проблему. Это обертка над Selenium, которая внедряет концепцию «умных ожиданий» (Smart Waits). Если вы просите Selenide кликнуть по кнопке, а ее еще нет, библиотека будет автоматически ждать ее появления (по умолчанию до 4 секунд), периодически опрашивая браузер.

    Код на Selenide лаконичен и легко читается. Библиотека сама управляет жизненным циклом браузера (открывает и закрывает его).

    Playwright: Новое поколение UI-тестирования

    Playwright от Microsoft — это современный инструмент, который стремительно вытесняет Selenium. Главное отличие — архитектура. Selenium использует HTTP-запросы для общения с браузером, что создает задержки. Playwright использует протокол Chrome DevTools Protocol (CDP), общаясь с браузером напрямую через веб-сокеты.

    Это делает Playwright невероятно быстрым. Кроме того, он изначально создавался для работы с современными асинхронными фреймворками (React, Vue, Angular) и умеет перехватывать сетевые запросы прямо в браузере.

    Возможность мокировать (подменять) сетевые ответы прямо в UI-тестах позволяет тестировать фронтенд изолированно от бэкенда, что радикально повышает стабильность проверок.

    Работа с базами данных

    В финтехе UI и API — это лишь верхушка айсберга. Истинное состояние системы хранится в базах данных (PostgreSQL, Oracle). QA Automation Engineer должен уметь подключаться к БД для двух целей:

  • Подготовка данных (Arrange): Создать пользователя с нужным балансом перед тестом.
  • Проверка состояния (Assert): Убедиться, что после перевода денег запись в таблице транзакций действительно появилась.
  • JetBrains Exposed: SQL через Kotlin DSL

    Вместо написания сырых SQL-запросов через стандартный JDBC, в Kotlin принято использовать легковесные ORM (Object-Relational Mapping) фреймворки. Один из лучших — Exposed от создателей языка Kotlin.

    Exposed позволяет описывать таблицы как объекты Kotlin и писать запросы, используя типобезопасный DSL.

    Если кто-то переименует колонку balance в базе данных и вы обновите объект Users, все тесты, использующие старое имя, перестанут компилироваться. Это спасает от долгих поисков ошибок во время выполнения.

    Инструменты проверок (Assertions)

    Встроенные проверки assertEquals(expected, actual)` из JUnit работают, но при падении теста выдают сухие логи. Для улучшения читаемости логов и кода используют специализированные библиотеки проверок.

    AssertJ и встроенные Kotest Assertions позволяют писать проверки в виде цепочек вызовов.

    Сравните стандартный подход:

    И подход с использованием Kotest Assertions:

    При падении второго варианта в логах будет четко написано: "Expected currency to be 'RUB' but was 'USD'", что моментально локализует проблему.

    Отчетность: Allure Framework

    Написать тесты — половина дела. Результаты их выполнения нужно презентовать команде. Читать консольные логи Bash неудобно. Индустриальным стандартом для генерации отчетов стал Allure Framework.

    Allure интегрируется с JUnit 5 и Kotest. Он собирает данные о прохождении тестов и генерирует красивый HTML-отчет с графиками, историей прогонов и подробным описанием каждого шага.

    Для разметки кода используются аннотации:

    В итоговом отчете бизнес-аналитик увидит не код, а понятный текст: «Создание платежа на сумму 1000 RUB», а в случае падения UI-теста к отчету автоматически прикрепится скриншот экрана.

    Интеграция с инфраструктурой (Bash и Git)

    Как все эти библиотеки работают вместе на сервере непрерывной интеграции (CI)?

    Ваш код хранится в системе контроля версий Git. Когда разработчик делает коммит, CI-сервер (например, GitLab CI) скачивает код и открывает командную строку Bash.

    В Bash запускается система сборки (обычно Gradle для Kotlin-проектов):

    Gradle читает конфигурацию, скачивает нужные версии библиотек (Kotest, Retrofit, Playwright), компилирует Kotlin-код в байт-код Java и запускает Test Runner. После завершения Allure собирает XML/JSON файлы с результатами и превращает их в веб-страницу.

    > Выбор инструментов определяет архитектуру фреймворка. Переход с Java + REST Assured + Selenium на Kotlin + Retrofit + Playwright позволяет сократить объем кода на 40% и ускорить выполнение тестов в 2-3 раза. > > QA.GURU

    Резюме подхода

    Для успешной автоматизации тестирования финтех-проектов на Kotlin рекомендуется следующий стек:

  • Управление запуском: Kotest (для использования всей мощи Kotlin) или JUnit 5 (для классического подхода).
  • API: Retrofit для строгой типизации и интеграции с корутинами.
  • UI: Playwright для высокой скорости и возможности перехвата сетевого трафика.
  • Базы данных: JetBrains Exposed для безопасного написания SQL-запросов.
  • Отчетность: Allure Framework для прозрачной коммуникации с бизнесом.
  • Освоив эти библиотеки, вы сможете решать любые задачи по автоматизации тестирования, создавая надежные и легко поддерживаемые проекты.

    8. Основы работы HTTP и REST

    Основы работы HTTP и REST

    Современные финтех-приложения — это сложные распределенные системы. Когда пользователь нажимает кнопку «Перевести деньги» в мобильном приложении, под капотом происходит обмен данными между десятками микросервисов. Мобильное приложение общается с сервером авторизации, сервером проверки баланса, антифрод-системой и платежным шлюзом.

    Фундаментом этого общения выступает протокол HTTP, а архитектурным стандартом построения таких систем является REST. Для QA Automation инженера понимание этих технологий критически важно: именно на уровне HTTP-запросов пишется 80% всех автотестов (API-тесты), которые обеспечивают стабильность банковских продуктов.

    Протокол HTTP: Клиент-серверная архитектура

    HTTP (HyperText Transfer Protocol) — это протокол передачи данных в интернете. Он работает по строгой модели «Клиент-Сервер».

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

    > HTTP — это протокол без сохранения состояния (Stateless). Сервер не запоминает предыдущие запросы клиента. Каждый новый запрос должен содержать всю информацию, необходимую для его выполнения.

    Эта особенность напрямую влияет на автоматизацию тестирования. Если вы пишете тест на проверку профиля пользователя, вы не можете просто отправить запрос «дай профиль», надеясь, что сервер помнит, как вы авторизовались минуту назад. Вы обязаны прикреплять токен авторизации к каждому запросу.

    Анатомия HTTP-запроса

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

  • Стартовая строка (Start Line): Содержит метод (что мы хотим сделать), URL (где находится ресурс) и версию протокола.
  • Заголовки (Headers): Метаданные запроса в формате «Ключ: Значение». Они объясняют серверу, кто делает запрос, в каком формате передаются данные и какой язык предпочитает клиент.
  • Пустая строка: Обязательный разделитель, указывающий на конец заголовков.
  • Тело запроса (Body): Сами данные. Присутствует не всегда (например, при получении данных тело не нужно).
  • Пример сырого HTTP-запроса на перевод средств:

    В этом примере заголовок Content-Type: application/json критически важен. Он говорит серверу: «Я прислал тебе данные в формате JSON, парси их соответствующим образом». Если QA-инженер забудет указать этот заголовок в автотесте, сервер может отклонить запрос, даже если само тело (JSON) написано абсолютно верно.

    Анатомия HTTP-ответа

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

  • Строка состояния (Status Line): Версия протокола, код состояния (число) и текстовое пояснение.
  • Заголовки ответа (Headers): Метаданные от сервера (например, размер ответа или дата).
  • Пустая строка.
  • Тело ответа (Body): Запрошенные данные или описание ошибки.
  • Пример ответа на наш перевод:

    !Схема взаимодействия клиента и сервера по протоколу HTTP с указанием структуры запроса и ответа

    HTTP-методы: Глаголы сетевого общения

    Метод в стартовой строке запроса указывает на намерение клиента. В финтехе правильное использование методов — это вопрос безопасности и предсказуемости системы.

    Основные методы

  • GET: Запрашивает данные. Не должен изменять состояние сервера. Пример: получить историю операций по карте.
  • POST: Отправляет данные для создания нового ресурса или запуска процесса. Пример: создать заявку на кредит.
  • PUT: Полностью заменяет существующий ресурс новыми данными. Если ресурса нет — может создать его. Пример: обновить все паспортные данные клиента.
  • PATCH: Частично обновляет ресурс. Пример: изменить только номер телефона в профиле, не трогая остальные поля.
  • DELETE: Удаляет ресурс. Пример: удалить привязанную карту.
  • Безопасность и Идемпотентность

    Для QA-инженера крайне важно понимать два свойства HTTP-методов: безопасность и идемпотентность. Это напрямую влияет на то, как мы пишем проверки (Asserts) и как система ведет себя при сбоях сети.

    Безопасный метод (Safe) — это метод, который не меняет состояние сервера (базы данных). Метод GET является безопасным. Вы можете запрашивать баланс счета 1000 раз подряд, и от этого количество денег на счету не изменится.

    Идемпотентный метод (Idempotent) — это метод, многократное выполнение которого приводит к тому же результату, что и однократное.

    Рассмотрим разницу на примере финтеха:

  • PUT идемпотентен. Если вы отправите запрос PUT /profile с новым адресом «Москва, ул. Ленина» один раз, адрес обновится. Если из-за сбоя сети телефон отправит этот же запрос еще 5 раз, итоговый результат в базе данных будет точно таким же: адрес останется «Москва, ул. Ленина».
  • POST не идемпотентен. Если вы отправляете POST /transfer на перевод 1000 рублей, и из-за лага интернета приложение дублирует запрос, с вашего счета спишется 2000 рублей.
  • Именно поэтому при тестировании POST-запросов QA-инженеры обязаны проверять механизмы защиты от дублей (например, передачу уникального ключа Idempotency-Key в заголовках, который сервер запоминает на короткое время, чтобы не провести платеж дважды).

    Коды состояния HTTP: Как сервер сообщает о результате

    Код состояния — это трехзначное число в ответе сервера. В автотестах проверка статус-кода — это всегда первый шаг (Assert). Если код не совпадает с ожидаемым, проверять тело ответа бессмысленно.

    Коды делятся на 5 классов по первой цифре:

    1xx: Информационные

    Запрос принят, процесс продолжается. В автотестах встречаются редко.

    2xx: Успех

  • 200 OK: Стандартный ответ для успешных GET, PUT, PATCH запросов.
  • 201 Created: Ресурс успешно создан. Ожидаемый ответ для правильного POST запроса (например, выпуск новой виртуальной карты).
  • 204 No Content: Запрос успешен, но серверу нечего вернуть в теле ответа. Часто используется для DELETE.
  • 3xx: Перенаправление

  • 301 Moved Permanently: Ресурс навсегда перемещен по новому URL.
  • 302 Found: Временное перенаправление (например, на страницу стороннего сервиса 3D-Secure для ввода SMS-кода при оплате).
  • 4xx: Ошибка клиента

    Эти коды означают, что автотест (или фронтенд) отправил некорректные данные. Ошибка на вашей стороне.
  • 400 Bad Request: Сервер не понял запрос из-за синтаксической ошибки (например, вы передали строку вместо числа в поле amount).
  • 401 Unauthorized: Вы не передали токен авторизации, или он просрочен. Сервер не знает, кто вы.
  • 403 Forbidden: Сервер знает, кто вы, но у вас нет прав на это действие. Пример: попытка обычного клиента вызвать API для блокировки чужого счета.
  • 404 Not Found: Ресурс не найден. Вы обратились по неверному URL или запросили транзакцию, которой не существует.
  • 409 Conflict: Запрос конфликтует с текущим состоянием сервера. Классический пример в финтехе: попытка перевести деньги со счета, который уже заблокирован.
  • 5xx: Ошибка сервера

    Эти коды означают, что клиент всё сделал правильно, но бэкенд сломался. Это баги, которые QA должен заводить в баг-трекер.
  • 500 Internal Server Error: Необработанное исключение в коде сервера (например, NullPointerException в Java-коде бэкенда).
  • 502 Bad Gateway: Микросервис, к которому обратились, работает как прокси и получил невалидный ответ от другого внутреннего сервиса.
  • 504 Gateway Timeout: Сервер не дождался ответа от базы данных или стороннего API (например, шлюза Visa/Mastercard) за отведенное время.
  • Архитектура REST API

    REST (Representational State Transfer) — это набор архитектурных правил для создания веб-сервисов. Если API построено по этим правилам, оно называется RESTful.

    В основе REST лежит понятие Ресурса. Ресурс — это любая сущность в системе: пользователь, банковский счет, транзакция, кредит.

    Главное правило REST: URL должен указывать на существительное (ресурс), а действие над ним определяется HTTP-методом (глаголом).

    Сравним плохой подход (RPC-стиль) и правильный REST-подход:

    | Действие | Плохой дизайн (Антипаттерн) | Правильный REST API | | :--- | :--- | :--- | | Получить баланс счета 123 | GET /getAccountBalance?id=123 | GET /accounts/123/balance | | Создать пользователя | POST /createNewUser | POST /users | | Удалить карту 456 | POST /deleteCard?cardId=456 | DELETE /cards/456 | | Обновить лимит карты 456 | POST /updateLimit | PATCH /cards/456/limit |

    В RESTful API структура URL образует иерархию. Например, путь /users/99/accounts/123 логично читается как «счет 123, принадлежащий пользователю 99».

    Формат данных: JSON

    Хотя REST не обязывает использовать конкретный формат данных, стандартом де-факто стал JSON (JavaScript Object Notation). Это легковесный текстовый формат, основанный на парах «ключ-значение».

    В Kotlin для работы с JSON используются библиотеки сериализации (например, Jackson или kotlinx.serialization). Они автоматически превращают JSON-ответы сервера в объекты Kotlin (Data Classes), с которыми удобно работать в автотестах.

    Практика: HTTP и REST в автотестах на Kotlin

    Как вся эта теория применяется в ежедневной работе QA Automation Engineer? Рассмотрим процесс написания API-теста.

    Допустим, нам нужно протестировать REST API создания нового банковского счета.

    Шаг 1: Изучение документации (Swagger/OpenAPI) Мы смотрим требования: для создания счета нужен POST запрос на /api/v1/accounts. В теле нужно передать валюту. Ожидаемый статус: 201 Created.

    Шаг 2: Подготовка Data Classes в Kotlin Мы описываем структуру тела запроса и ожидаемого ответа с помощью классов Kotlin. Это обеспечивает строгую типизацию.

    Шаг 3: Написание теста (паттерн AAA) Используя библиотеку для HTTP-запросов (например, REST Assured или Retrofit), мы формируем запрос, отправляем его и проверяем ответ.

    В этом примере мы явно видим применение теории на практике: мы указали правильный HTTP-метод (POST), передали обязательные заголовки (Authorization для решения проблемы Stateless и Content-Type для указания формата JSON), проверили RESTful статус-код (201) и провалидировали бизнес-логику в теле ответа.

    Понимание основ HTTP и REST позволяет инженеру по автоматизации не просто писать код по шаблону, а осознанно проектировать тесты, понимать причины падений (отличать ошибку клиента 4xx от ошибки сервера 5xx) и эффективно локализовать дефекты в микросервисной архитектуре финтех-приложений.

    9. Автоматизация тестирования API на Kotlin

    Автоматизация тестирования API на Kotlin

    В финтех-отрасли цена ошибки критически высока. Некорректно работающий интерфейс мобильного приложения может вызвать раздражение пользователя, но ошибка на уровне бэкенда способна привести к финансовым потерям, утечке данных и штрафам от регуляторов. Именно поэтому автоматизация тестирования API (Application Programming Interface) является фундаментом обеспечения качества банковских продуктов.

    Тестирование API позволяет проверять бизнес-логику напрямую, минуя графический интерфейс. Это делает тесты быстрыми, стабильными и независимыми от изменений дизайна. Для реализации таких проверок индустрия стандартизировала связку языка Kotlin и библиотеки REST Assured.

    Архитектура тестового фреймворка

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

  • Язык программирования (Kotlin): Обеспечивает строгую типизацию, лаконичный синтаксис и объектно-ориентированную структуру.
  • Тестовый движок (JUnit 5): Управляет жизненным циклом тестов. Он решает, какие тесты запускать, в каком порядке, и собирает результаты (упал/прошел).
  • HTTP-клиент (REST Assured): Формирует сетевые запросы к серверу и принимает ответы.
  • Сериализатор (Jackson): Переводит объекты Kotlin в текстовый формат JSON перед отправкой и выполняет обратный процесс при получении ответа.
  • Система отчетности (Allure): Генерирует визуально понятные HTML-отчеты с графиками и логами для бизнеса и команды разработки.
  • Система сборки (Gradle): Скачивает нужные библиотеки и позволяет запускать тесты из командной строки Bash.
  • !Схема взаимодействия компонентов тестового фреймворка

    Подготовка инфраструктуры: Gradle и зависимости

    Любой проект начинается с конфигурации системы сборки. В экосистеме Kotlin стандартом является Gradle с использованием Kotlin DSL (файл build.gradle.kts). Этот файл описывает, какие внешние библиотеки нужны нашему проекту для работы.

    После создания этого файла инженер использует систему контроля версий Git, чтобы зафиксировать базовую настройку: выполняет команды git add build.gradle.kts и git commit -m "Setup test framework dependencies".

    Сериализация и Data Classes

    При общении с REST API данные передаются в формате JSON. Работать с сырым текстом в коде неудобно и чревато опечатками. Здесь на помощь приходит Сериализация — процесс перевода объекта в памяти программы в формат, пригодный для передачи по сети (JSON), и Десериализация — обратный процесс.

    В Kotlin для описания структуры JSON используются Data Classes (классы данных). Библиотека Jackson автоматически сопоставляет ключи из JSON с полями класса.

    Рассмотрим пример финтех-операции: перевод средств. Нам нужно отправить POST-запрос с указанием счета списания, счета зачисления и суммы.

    > Использование значений по умолчанию (как currency = "RUB") позволяет сократить код при создании объектов в тестах, если в 90% случаев мы переводим рубли.

    Написание первого API-теста с REST Assured

    Библиотека REST Assured использует подход BDD (Behavior-Driven Development) с ключевыми словами Given, When, Then. Этот синтаксис идеально ложится на паттерн AAA (Arrange, Act, Assert), который мы рассматривали ранее.

  • Given (Arrange): Подготовка данных. Установка URL, заголовков, тела запроса.
  • When (Act): Выполнение действия. Указание HTTP-метода и эндпоинта.
  • Then (Assert): Проверка результата. Валидация статус-кода и тела ответа.
  • Напишем тест, проверяющий успешный перевод средств:

    Разберем ключевые моменты этого кода:

  • Метод .contentType(ContentType.JSON) автоматически добавляет заголовок Content-Type: application/json, сообщая серверу формат данных.
  • Метод .header(...) передает токен авторизации. Без него сервер вернет ошибку 401 Unauthorized.
  • Функция ` when() взята в обратные кавычки, так как when является зарезервированным словом в Kotlin (оператор ветвления).
  • Для проверок используется библиотека AssertJ (assertThat...). Она предоставляет удобный текучий интерфейс (fluent interface) и генерирует понятные сообщения об ошибках, если тест падает.
  • Продвинутые техники: Параметризованные тесты

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

    JUnit 5 предоставляет механизм Параметризованных тестов (@ParameterizedTest). Он позволяет написать логику теста один раз и запустить ее с разными наборами данных.

    Рассмотрим проверку комиссии за перевод в зависимости от суммы. Допустим, бизнес-требования гласят:

  • Перевод до 10 000 руб. — комиссия 0 руб.
  • Перевод от 10 000 до 50 000 руб. — комиссия 50 руб.
  • Перевод свыше 50 000 руб. — комиссия 1% от суммы.
  • При запуске этого кода JUnit 5 выполнит тест 5 раз, подставляя значения из аннотации @CsvSource. Если логика расчета комиссии на сервере сломается для сумм свыше 50 000, упадут только последние два прогона, а первые три будут зелеными. Это мгновенно локализует проблему для разработчиков.

    Борьба с нестабильностью: Flaky тесты и независимость данных

    Одной из главных проблем в автоматизации являются Flaky тесты (моргающие тесты) — тесты, которые то проходят, то падают без изменений в коде. В 80% случаев причина кроется в зависимости от тестовых данных.

    Представьте, что в тесте перевода средств мы захардкодили номера счетов. Тест списывает 5000 рублей. Если запустить этот тест 10 раз подряд, на балансе счета отправителя закончатся деньги, и на 11-й раз сервер вернет ошибку 409 Conflict (Недостаточно средств). Тест упадет, хотя API работает абсолютно корректно.

    Золотое правило автоматизации API: Тест должен сам создавать данные для своей работы и убирать за собой.

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

  • Вызвать API создания нового пользователя.
  • Вызвать API открытия двух новых счетов для этого пользователя.
  • Вызвать API пополнения первого счета на нужную сумму (или сделать это через прямое подключение к базе данных).
  • Выполнить целевой API-запрос перевода средств.
  • Проверить результат.
  • (Опционально) Вызвать API удаления пользователя, чтобы не засорять базу.
  • Такой подход делает тесты атомарными и независимыми. Их можно запускать параллельно в 10 потоков, и они никогда не пересекутся по данным.

    Интеграция с инфраструктурой: Bash, Allure и логирование

    Автотесты не приносят пользы, если они лежат только на ноутбуке инженера. Они должны запускаться автоматически на удаленном Linux-сервере (CI/CD) при каждом обновлении кода разработчиками.

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

    ./gradlew clean test

    Эта команда очищает старые результаты сборки и запускает все классы с аннотациями @Test.

    Логирование запросов

    Когда тест падает на удаленном сервере, у инженера нет возможности поставить точку останова (breakpoint). Единственный способ понять причину — изучить логи. REST Assured позволяет автоматически логировать все запросы и ответы с помощью фильтров.

    Теперь, если тест упадет, в консоли Bash (stdout) будет распечатан полный HTTP-запрос (с заголовками и телом) и полный ответ сервера. Инженер сможет использовать команду grep в логах сервера, чтобы найти transactionId упавшего запроса и отследить его путь по микросервисам.

    Отчетность Allure

    Читать сырые логи в консоли неудобно, особенно менеджерам или аналитикам. Для визуализации результатов используется фреймворк Allure. Он интегрируется с JUnit 5 и REST Assured, перехватывая данные о выполнении тестов.

    Для генерации отчета после прогона тестов в Bash выполняется команда:

    ./gradlew allureReport

    Allure создает красивую HTML-страницу, где видно процент успешных тестов, время их выполнения, а к каждому упавшему тесту прикреплен JSON запроса и ответа. Использование аннотаций @Step` в Kotlin позволяет разбивать сложные тесты на читаемые шаги прямо в отчете, превращая код в живую документацию продукта.

    Владение связкой Kotlin, REST Assured, JUnit 5 и Allure позволяет QA Automation инженеру выстроить надежный процесс контроля качества API, который экономит сотни часов ручного регрессионного тестирования перед каждым релизом финтех-продукта.