Привет! Меня зовут Бальцер Вадим, я QA-инженер в команде android направления Банки.ру. Сегодня поделюсь нашим опытом внедрения автотестов обратной совместимости и интеграции их в CI. 

В материале расскажу: 

  • Зачем нашей команде понадобились автотесты для контроля обратной совместимости API, почему мы выбрали Postman CI.  

  • С чего начали: базовые вещи + полезные советы для начинающих. 

  • Есть ли жизнь за гранью проверок схемы.

  • CI и интеграция с ТестОпс.  

  • К чему пришли и какие перспективы видим.

Также добавлю примеры примеры кода и небольшие советы, которые могут пригодиться, если вы еще только присматриваетесь к Postman как к инструменту автоматизации проверок API.

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

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

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

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

  • Функциональность. Инструмент должен был быть перспективным, предполагать реализацию более сложных сценариев, а не только базовые проверки API-схем. 

  • Возможность запуска через CLI для интеграции с нашим CI/CD. Это важное требование. Процесс тестирования мы хотели автоматизировать. чтобы не пришлось ничего устанавливать вручную и перепроверять. 

  • Совместимость с разноообразным стэком на наших бэкендах.

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

  • Простая интеграция с Тест Опс.

  • Автоматическая генерация отчетов.

Существующие инструменты для теста API

Инструментов разработки автотестов для API на рынке достаточно. 

Postman и Newman.
Postman — один из самых популярных инструментов для работы с API, который также поддерживает написание автотестов. Инструмент предлагает удобный интерфейс для создания запросов, тестов и их коллекций. В связке с Newman — CLI-инструментом для запуска Postman коллекций — можно легко интегрировать тесты в CI/CD, автоматизировав их выполнение. Есть ньюансы — на них остановлюсь в следующих разделах. 

REST Assured.
REST Assured — библиотека для тестирования REST API на Java, которая де-факто стала стандартом в среде java-разработчиков. Она позволяет писать тесты в привычном Java-стиле, что упрощает интеграцию с существующими фреймворками и инструментами для тестирования. REST Assured особенно популярен среди команд, которые уже используют Java и хотят минимизировать зависимость от сторонних инструментов.

JUnit и TestNG.
Эти тестовые фреймворки часто используются в связке с REST Assured или другими библиотеками для тестирования API. JUnit и TestNG предоставляют мощные инструменты для организации и управления тестами, а также интеграции их с различными системами сборки, такими как Maven или Gradle.

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

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

Почему выбрали Postman CI

Проанализировав возможные варианты, остановились на Postman. Он соответствовал всем нашим требованиям: среди QA-инженеров известен, автотесты можно писать на JS, также есть библиотека Newman, которая позволяет интегрировать инструмент в CI. 

Правда, есть и минусы:

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

Эти минусы для нас были приемлемыми и решаемыми. Мы сразу договорились, что на этапе ревью выгружаем коллекцию UI postman и проверяем pull-request там, при этом оставляя комментарии в интерфейсе битбакета. 

Первые шаги 

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

На первом этапе мы решили покрыть тестами непростой, но критически важный функционал: тесты авторизации и тесты мастера подбора кредитов (МПК), который по сути представляет из себя анкету для подбора предложений. Авторизация — один из наиболее используемых функционалов, многие методы закрыты для неавторизованного пользователя. А МПК один из наших флагманских разделов. Плюс он достаточно комплексный: работа с ним помогла бы освоить все необходимые навыки для написания тестов в Postman.

Учимся использовать переменные и упрощаем схему ответа

Начнем с авторизации. 

Все достаточно стандартно для oauth 2.0. У нас есть два запроса:

  • запрос на отправку кода на телефон POST /1.0/auth/passwordless/start; 

  • запрос на получения access токена POST /1.0/oauth/token. 

Нам необходимо проверить соответствие ответа контракту. Пример запроса и ответа для  POST /1.0/auth/passwordless/start:

Очевидным решением будет написать следующий тест:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});
pm.test("Valid schema", function () {
    const responseJson = pm.response.json();
    pm.expect(responseJson.id).to.be.a('string');
    pm.expert(responseJson.attempt_options.ttl).to.be.a('number');
    pm.expert(responseJson.attempt_options.max_count_attempts).to.be.a('number');
    pm.expert(responseJson.attempt_options.timeout_request).to.be.a('number');
    pm.expert(responseJson.attempt_options.timeout_reentering).to.be.a('number');
});

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

Далее у нас есть запрос на получение токена:

Пишем тест по аналогии с прошлым запросом:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});
pm.test("Valid schema", function () {
    const responseJson = pm.response.json();
    pm.expect(responseJson.access_token).to.be.a('string');
    pm.expect(responseJson.refresh_token).to.be.a('string');
    pm.expect(responseJson.token_type).to.be.a('string');
    pm.expect(responseJson.expires_in).to.be.a('number');
});

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

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

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

В рамках статьи мы будем пользоваться глобальными переменными (они же — переменные окружения) и переменными коллекции.

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

Сохранить данные в переменную коллекции можно функцией pm.collectionVariables.set("auth_request_id", response_json.id) (не забывайте про область видимости переменных), а использовать в теле запроса можно, указав название переменной в формате {{variable_name}}. 

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

Так как коллекция авторизации нужна не только для проверки работы методов авторизации, но и для получения токена, то аксесс и рефреш токены необходимо сохранить в переменные окружения, что можно сделать функциями pm.globals.set("access_token", access_token) и pm.globals.set("refresh_token", refresh_token) соответственно.

Tip 1. Чтобы определить, куда лучше сохранить переменную — на уровне коллекции или окружения, просто задайтесь вопросом «А может ли она мне пригодиться в рамках других коллекций»?

Почитать про переменные можно тут https://learning.postman.com/docs/sending-requests/variables/variables-intro/

С переменными разобрались, теперь избавиться от дублирования кода. Для этого тест с проверкой кода ответа на 200 вынесем в блок Post-response (в ранних версиях Test), тогда он будет выполняться после каждого запроса до выполнения тестов из самого запроса.

Переходим к самому главному: проверке схемы ответа. Использование expect громоздко и неудобно, а в случае ответа с большим количеством параметров превращается в пытку. Тут нам на помощь приходит проверка pm.response.to.have.jsonSchema(schema). Она позволяет сравнивать полученный ответ с схемой.

Подробнее про схемы можно прочитать туть https://blog.postman.com/what-is-json-schema/

Tip 2. При составлении схемы ответа параметры, имеющие базовый тип, лучше описывать в одну строку, а каждую новую сущность выносить в отдельную переменную. Первое позволит сохранить читаемость схем, а второе — переиспользовать схемы для одинаковых сущностей. Например, если у вас есть объект token, который приходит в нескольких запросах, то его схема может выглядеть следующим образом:

auth_token_schema = {
    'type': 'object',
    'required': [],
    'properties': {
        'access_token': { 'type': 'string' },
        'refresh_token': { 'type': 'string' },
        'token_type': { 'type': 'string' },
        'expires_in': { 'type': 'number' }
    }
};

Tip 3. Схемы ответов можно хранить на уровне коллекции в Pre-req. Это позволяет переиспользовать повторяющиеся сущности в рамках других запросов этой коллекции. 

Таким образом получившаяся коллекция с тестами будет иметь вид:

attempt_options_schema = {
    'type': 'object',
    'required': ['ttl', 'max_count_attempts', 'timeout_request', 'timeout_reentering'],
    'properties': {
        'ttl': { 'type': 'integer' },
        'max_count_attempts': { 'type': 'integer' },
        'timeout_request': { 'type': 'integer' },
        'timeout_reentering': { 'type': 'integer' }
    }
};
auth_start_schema = {
    'type': 'object',
    'required': ['id', 'attempt_options'],
    'properties': {
        'id': { 'type': 'number' },
        'call_id': { 'type': ['string', 'null'] },
        'attempt_options': attempt_options_schema
    }
};
auth_token_schema = {
    'type': 'object',
    'required': [],
    'properties': {
        'access_token': { 'type': 'string' },
        'refresh_token': { 'type': 'string' },
        'token_type': { 'type': 'string' },
        'expires_in': { 'type': 'number' }
    }
};
pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

Запрос POST /1.0/auth/passwordless/start:

pm.test("Valid schema", function () {
    pm.response.to.have.jsonSchema(auth_start_schema)
    pm.collectionVariables.set("request_id", response_json.id);
});

Запрос POST auth/token:

pm.test("Valid schema", function () {
    pm.response.to.have.jsonSchema(auth_token_schema)
    pm.globals.set("access_token", pm.response.toJSON().access_token);
    pm.globals.set("refresh_token", pm.response.toJSON().refresh_token);
});

Теперь тесты выглядят значительно легче. 

Внедряем динамическое изменение тела запроса 

Теперь же перейдем к МПК (анкете для подбора кредита). Из интересного на данном этапе я расскажу о: 

  • динамическом заполнении тела запроса и изменении query параметров;

  • динамическом изменении порядка запросов;

  • доступ к собственным функциям из любого запроса;

  • ретраи запросов по таймеру и количество запросов.

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

Нам нужно было проверить: 

  • корректность возвращаемых данных на шагах анкеты; 

  • получение конечного статуса выдачи за определенный срок; 

  • получение выдачи пользователем. 

Интересная особенность встречается уже на первом шаге. Так как у анкеты могут быть различные цели, то и тело запроса может содержать разные данные. Создавать несколько разных запросов на один урл не целесообразно. Поэтому мы решили хранить все возможные данные просто как key-value в js объекте и заполнять тело запроса перед его отправкой. Происходит это в Pre-Request запроса и выглядит следующим образом:


const bodies = {
    1: {
        "purposeCode": 1,
        "amount": 300001,
        "period": 36,
        "periodUnit": 6,
        "depositCode": 3
    },
    3: {
        "purposeCode": 3,
        "amount": 300003,
        "period": 36,
        "periodUnit": 6,
        "expensesSum": 30000
    },
    4: {
        "purposeCode": 4,
        "amount": 300004,
        "periodUnit": 6,
        "period": 36
    }
}
pm.request.body.raw = ` { ${bodies[pm.collectionVariables.get('currentPurpose')]} }`;
if (pm.collectionVariables.get('canFinalizeFlowChecking') === '1') {
    pm.request.addQueryParams('canFinalize=true');
}

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

После отправки первого шага последующий не определен заранее, поэтому нам нужно было научиться динамически менять последовательность запросов. По умолчанию в Postman запросы выполняются в том порядке, в котором они расположены в дереве коллекции. Контролировать порядок запросов в рамках коллекции можно с помощью метода postman.setNextRequest(“Name of my request”) (прочитать подробнее можно тут). Однако нам также необходима функция, которая вернет нужный запрос. Мы реализовали это через обычный switch-case :

utils = {
    nextStep: function (stepId) { // This function helps determine next correct response
        switch (stepId) {
            case 'PurposeAmountPeriod':
                return "/2.0/credit-master/step/get/purposeamountperiod/";
            case 'Contacts':
                return "/2.0/credit-master/step/get/contacts/";
            case 'Passport':
                return "/2.0/credit-master/step/get/passport/";
            case 'Job':
                return "/2.0/credit-master/step/get/job/";
            case 'AdditionalInformation':
                return "/2.0/credit-master/step/get/additionalinformation/";
            case 'final':
                return "/2.0/credit-master/result/is_ready/";
            case 'results':
                return "/2.0/credit-master/results/";
            case 'lastStep':
                return "/2.0/credit-master/anketa/init/";
            default:
                throw new Error('stepId ведет на неизваестный шаг ${stepId}');
        }
    }
}

В дальнейшем мы вызываем postman.setNextRequest(utils.nextStep(id)) для определения следующего запроса.

Как вы могли заметить, функция nextStep хранится внутри объекта utils. Сейчас объясню, зачем мы это сделали. 

Так как порядок шагов не определен, то после каждого запроса нам необходимо использовать функцию nextStep, но инициализировать её внутри каждого запроса коллекции — значит дублировать код. Мы думали, как инкапсулировать эту логику и в итоге пришли к банальному решению — вынести нужные функции в глобальный объект, который хранит все хелперы. Мы создали utils в Pre-Request коллекции. Там мы сохраняем все необходимые нам функции, которые и будем использовать в различных запросах.

После прохождения всех шагов анкеты пользователем запускается асинхронный процесс подбора предложений. Это значит, нам нужно дождаться готовности результатов, статус которых мы узнаем с помощью запроса /2.0/credit-master/result/is_ready/. 

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

if (status === 'ready') {
    console.log('Текущий статус: Успех');
    pm.collectionVariables.set('isReadyTryCount', 0);
    const nextStep = utils.nextStep('results');
    postman.setNextRequest(nextStep);
} else if (status === 'wait' && pm.collectionVariables.get('isReadyTryCount') < 10) {
    pm.collectionVariables.set('isReadyTryCount', parseInt(pm.collectionVariables.get('isReadyTryCount')) + 1);
    console.log(`Текущий статус: ${status}, попытка ${pm.collectionVariables.get('isReadyTryCount')} из 10.`);
    setTimeout(() => {
        const nextStep = utils.nextStep('final');
        postman.setNextRequest(nextStep);
    }, 4500);
} else {
    throw new Error("Success status not commint in time");
}

Этот блок кода позволяет нам вызывать запрос /2.0/credit-master/result/is_ready/ каждые 4.5 секунды до десяти раз или же до получения успешного статуса. Данные были выбраны с командой разработки продукта: если результаты готовятся больше, значит что-то пошло не так.

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

Чему научились в процессе 

  • Работать с коллекциями запросов и писать тесты к данным запросам.

  • Проверять схему ответа от сервера. Освоили переиспользование схемы, хранящейся на уровне коллекции, в запросах этой же коллекции 

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

  • Освоили работу с Pre-request Scripts. Например, заполнение и изменение данных для запроса перед его отправкой

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

  • Во время ревью ПР-ов мы также столкнулись с тем, что выгруженные из Postman коллекции неудобно ревьюить, поэтому в команде договорились импортировать коллекции в Postman и анализировать их там, но при этом сами комменты писать в ПР.  

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

Интеграция с CI: успехи и боли  

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

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

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

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

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

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

  • Выбор тестов для выполнения или игнорирования с помощью параметров RUN_TEST_LIST и IGNORE_TEST_LIST. Не все сервисы выкладывались каждый день и не было смысла запускать тесты для функционала, который не обновлялся.

  • Запуск тестов на тестовых площадках отдельных команд.

  • Настройка тестового номера через план, что позволило более гибко управлять тестами.

Запуск тестов на стороне команд разработки 

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

Улучшение контроля и стабильности тестов

Мы также начали дробить тесты, разделяя их выполнение на несколько итераций, а не прогоняя все сразу. Этот подход позволил лучше контролировать процесс, повысил скорость выполнения тестов и их стабильность. Другие команды также решили перенять наш опыт, поэтому появилась необходимость выбирать окружение, на котором запускались тесты. Контроль за процессом был реализован через переменную BUILD_HOST, в которой можно было указать среду для запуска тестов,что добавило еще больше гибкости в управление тестами. Хотя данный шаг требовал дополнительного времени на настройку, его положительное влияние на общий процесс тестирования оказалось весьма значительным.

Выбор номер телефона для тестирования

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

if (process.env.PHONE_NUMBER) {
    globals.values = [...globals.values, {
        "key": "phone_number",
        "value": process.env.PHONE_NUMBER,
        "enabled": true
    }]
}

Другие кейсы, реализованные с помощью Postman CI: расширенные тесты

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

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

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

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

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

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

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

Для оптимизации добавили переменную в CI и распределили тесты по папкам: одни тесты связаны с основным процессом, другие — с расширенными проверками. Расширенные тесты запускаются во время регрессии или по расписанию, что позволяет эффективно управлять ресурсами и временем выполнения тестов.

Развитие инструмента: тесты в тестовых площадках и интеграция с Allure

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

Процесс работает следующим образом:

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

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

  • Если тест падает, команда оперативно разбирается с проблемой и исправляет баги.

На этом же этапе мы внедрили интеграцию с Allure. Раньше результаты тестов приходилось просматривать напрямую в нашем CI через логи, что занимало много времени и было неудобно. Для интеграции мы настроили Newman для генерации отчетов в формате, совместимом с Allure. Далее уже сам Allure собирает данные из Newman и предоставляет удобный интерфейс для анализа результатов прогона.

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

Так же теперь мы не заводим тесты вручную в Allure — вместо этого достаточно добавить аннотации в код тестов. Это обеспечивает бесшовную интеграцию и экономит время на настройку.

// @allure.label.product=Постман тесты
// @allure.label.suite=Авторизация
pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

Выводы и рекомендации

Интеграция Postman-CI в наш процесс тестирования оказалась успешной и принесла отличные результаты. На данный момент около 95% методов API покрыто автоматизированными тестами, что позволяет нам эффективно контролировать доступность методов и соблюдение API-контрактов.

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

Основные результаты:

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

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

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

  4. Повышение навыков  QA инженеров: Большинство без проблем освоило js на базом уровне, необходимом для постмена и уже сами покрывают свои сервисы

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

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