Pull to refresh

Postman и End-2-End тестирование

Введение

Всем привет. В этой статье я расскажу, как использовал Postman для E2E тестов API, и что из этого вышло.

Про сам Postman как средство для тестирования API написано довольно много статей как и на Хабре, так и просто в вебе. А еще у них есть отличная документация и обучающий сайт (рекомендую заглянуть, снимает примерно 85% всех вопросов по использованию этого инструмента), поэтому здесь я буду акцентировать внимание именно на сценариях тестирования.

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

Что такое E2E тест в постмане? Это коллекция из запросов из разных API, которую можно запускать либо через нативный Postman-Runner, либо с помощью консольного расширения Newman. Ассертится все это через тесты на JS с использованием Chai библиотеки, которая поддерживается постманом.

Итак, начнём. Все переменные в тесте я буду создавать на уровне коллекции, чтобы не нагромождать env и global списки - там их итак много, и чтобы эти тесты можно было запускать из разных environment.

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

Я использую методы из 3-х разных апи, с разными кредитами для получения bearer token, и поэтому не получается вынести аутентификацию на уровень коллекции. 

Тест-кейс


1. GET запрос на сервер возвращает общий список тренировок на нашем тренажёре, и их кол-во у определенного пользователя. У тренажера несколько типов тренировок, в ответе нас интересует тип тренировки Fast:

{
    "trainingCountAll": 101,
    "trainingCountSprint": 9,
    "trainingCountFast": 28,
    "trainingCountBrave": 2,
    "trainingCountBurner": 41,
    "trainingCountFreeTraining": 21,
    "trainingCountPulsingRun": 0
}

В тестах (все тесты я буду делать во вкладке "Tests") помимо стандартных кейсов на статус-код и наличие key/value я создаю переменную на уровне коллекции и записываю туда значение из trainingCountFast. Это кол-во тренировок, потом буду проверять, что после новой счетчик увеличился на 1.

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});
 var jsonData = pm.response.json();
pm.test("The response is (JSON)", () => {
  pm.expect(jsonData).to.be.an("object");
});
pm.test("Asserting data type of the response", () => {
  pm.expect(jsonData.trainingCountAll).to.be.a("number");
  pm.expect(jsonData.trainingCountSprint).to.be.a("number");
  pm.expect(jsonData.trainingCountFast).to.be.a("number");
  pm.expect(jsonData.trainingCountBrave).to.be.a("number");
  pm.expect(jsonData.trainingCountBurner).to.be.a("number");
  pm.expect(jsonData.trainingCountFreeTraining).to.be.a("number");
  pm.expect(jsonData.trainingCountPulsingRun).to.be.a("number");
  });
//create collection variable
 pm.collectionVariables.set("test_traneCountFast", jsonData.trainingCountFast);

2. PUT запросом отсылаю на сервер тренировку, body запроса выглядит так:


   "averagePower":25.45225604650447,
   "calories":{{test_fastCalCount}}.453627902654,
   "distance":2053.9938944515175,
   "duration":500,
   "heartRateAverage":94.38842978139785,
   "heartRateMax":112.30478395061728,
   "octaneScore":0.0,
   "peakPower":40.02082868,
   "resistanceMax":36,
   "rpmAverage":70.838,
   "rpmMax":92.0,
   "start":{{timeSecondAgo}},
   "totalPower":12776.128023252235,
   "type":"FAST",
   "trainderId":{{trainerId}},
   "trainingTelemetryList":[...]

В trainingTelemetryList массив из 100+ объектов на 5 тыс. строк, целиком решил тут не выкладывать).

В Calories у меня стоит переменная test_fastCalCount которую я заранее объявил в Pre-Request Scripts, и записал в нее значение из переменной, содержащей значения калорий в предыдущей тренировке + 1. Знаки после запятой нужны для проверки округления.

var cal = pm.collectionVariables.get("test_fastCal");
pm.collectionVariables.set("test_fastCalCount", +cal+1);

3. GET запросом опять возвращаю список всех поездок, и получаю:

{
    "trainingCountAll": 102,
    "trainingCountSprint": 9,
    "trainingCountFast": 29,
    "trainingCountBrave": 2,
    "trainingCountBurner": 41,
    "trainingCountFreeTraining": 21,
    "trainingCountPulsingRun": 0
}

Во вкладке Tests проверяю, что значение trainingCountFast увеличилось на 1. Заодно этот тест проверит, что округление знаков после запятой работает как надо (т.е. в данном случае не увеличит значение).

var jsonData = pm.response.json();
var countEnd = (jsonData.trainingCountFast);
pm.test("Assert trainingCountFast + 1 value", function () {
    pm.expect(countEnd).to.equal(pm.collectionVariables.get("test_traneCountFast)+1)
});

4. GET запросом я беру с сервера информацию о калориях по типу тренировки fast, и получают такой ответ:

{
    "trainerType": "FAST",
    "best": "15.3k",
    "last": "501",
    "title": "Calories include trainers"
}

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

var jsonData = pm.response.json();
var last = (jsonData.last);
var lastNumber = Number(last);

pm.test("Assert last value is correctly", function () {
    pm.expect(lastNumber).to.equal(pm.collectionVariables.get("test_fastCalCount"))
    });

5. Итак, корректное сохранение калорий я проверил, сохранение тренировки проверил, счетчики тренировок увеличиваются, и настало время тренировку удалить. Но для этого нужно получить ее ID, и я использую GET запрос, который возвращает все тренировки с типом fast:

{
    "content": [
        {
            "id": 18822,
            "type": "FAST",
            "start": 1617015648890,
            "duration": 500,
            "distance": 2054.0,
            "heartRateAverage": 94.0,
            "heartRateMax": 112.0,
            "calories": 501.0,
            "averagePower": 25.0,
            "peakPower": 40.0,
            "totalPower": 12776.0,
            "resistanceMax": 36,
            "octaneScore": 0.0,
            "rpmAverage": 71.0,
            "rpmMax": 92.0,            
        },
        {}...

В тестах берем ID, и поскольку тестовый сценарий идет последовательно, то я могу быть уверен, что последняя тренировка - это моя. Это stage, и шанс того, что кто-то кроме меня в эту же секунду откатает тренировку с моим trainerId крайне мал.

var jsonData = pm.response.json();
pm.collectionVariables.set("test_traningIdFast", jsonData.content[0].id);

6. DELETE запросом удаляем выбранную тренировку, параметром в URL передаем test_traningIdFast. DEL запрос нам ничего кроме 200 не возвращает, поэтому просто проверяем его.

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

7. Далее GET запрос, который возвращает кол-во тренировок, нужно убедиться, что после удаления тренировки счетчик сделал -1. Тесты на это:

var jsonData = pm.response.json();
var countEnd = (jsonData.trainingCountFast);
pm.test("Assert trainingCountFast - 1 value", function () {
    pm.expect(countEnd).to.equal(pm.collectionVariables.get("test_traneCountFast)
});

Итого у нас получилась коллекция из 7-ми запросов. Ее можно прогонять как в постмане, так и экспортировать в JSON, и запускать через командную строку, используя newman.

Что можно сделать еще: поскольку Postman запускает запросы последовательно, и тут вся цепочка взаимозависима, если один запрос упадет, то продолжать ранить остальные не имеет смысла, поэтому надо добавить простую if/else конструкцию во вкладке "Tests", которая будет тормозить запрос при определенных условиях.

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

if (pm.response.code === 200) {pm.test("Body is empty", function () {
  pm.response.to.have.body("");
});
}
else {
  postman.setNextRequest(null);
}
});

Поскольку PUT запрос ничего не отдает кроме статус-кода, то в случае успешного выполнения будет тест на пустое тело. Если статус-код изменится, то runner прекратит дальнейшие запросы.

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.