Fetch для QA: как тестировать API прямо в DevTools, не открывая Postman

в 9:47, , рубрики: api, DevTools, Postman, qa, тестирование

Недавно у меня была итерация тестирования. Задача — проверить функционал, для которого нужно было создать 50+ сущностей. Однотипных, с разными названиями.

Обычно такое делаю через интерфейс. Открываю форму, заполняю поля, сохраняю. Потом ещё раз. И ещё. Пятьдесят раз. Я замерял: на одну сущность уходит примерно 20 секунд. Умножаем на 50 и получается больше 15 минут однотипных действий. А времени на итерации всегда мало.

Я уже собирался начать делать это руками, но вспомнил про fetch. Ту самую функцию, которая живёт в DevTools на вкладке Console. Пару минут на скрипт и 50 сущностей созданы за минуту. Итог: я сэкономил больше 15 минут. И заодно освоил инструмент, который теперь использую регулярно.

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

Немного про Fetch

Fetch — это встроенная в браузер функция JavaScript для отправки HTTP‑запросов. Ставить ничего не нужно: она уже есть в DevTools на вкладке Console. В Postman ты жмёшь Send. Здесь ты сам пишешь «Send» — вот так:

fetch('https://test.com/api/users')

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

Первый: fetch возвращает Promise. Когда ты пишешь fetch(url), JavaScript не ждёт, пока сервер ответит. Он сразу отдаёт объект Promise — этакую «расписку»: мол, запрос ушёл, ответ будет, но чуть позже. Представь: ты нажал Enter, а ответа нет. Секунду нет. Две нет. Это не баг — это Promise в действии. Дождаться ответа можно через.then() или await. Возможно, тебе покажется непонятно, но в шаблоне это будет одна готовая строчка. Просто скопируешь и всё.

У Promise три состояния: pending (ожидает), fulfilled (выполнено), rejected (отклонено). Пока запрос идет на сервер и обратно — Promise в состоянии pending. Как только ответ пришёл — становится fulfilled. Если что-то сломалось — rejected.
У Promise три состояния: pending (ожидает), fulfilled (выполнено), rejected (отклонено). Пока запрос идет на сервер и обратно — Promise в состоянии pending. Как только ответ пришёл — становится fulfilled. Если что‑то сломалось — rejected.

Второй: для fetch успех — это любой ответ от сервера. Даже 404 или 500. Функция не «упадёт» и не вернет ошибку — спокойно отдаст ответ. Запомни простое правило: fetch — «оптимист», он рад любому ответу. А мы, QA, — пессимисты:) В шаблоне мы отдельно выведем status.

504

504

Всё, с теорией закончили. Дальше практика: покажу шаблон POST‑запроса, который за пару минут можно адаптировать под любой свой эндпоинт.

Шаблон fetch для POST‑запроса

Теперь, когда ты понимаешь (надеюсь, что я понятно объяснил), что такое fetch, давай напишем первый рабочий запрос. Я напишу пример для POST запроса. Тебе останется подставить свои значения.

fetch(‘<URL_вашего_эндпоинта>’, {
method: ‘POST’,
headers: {
‘Content-Type’: ‘application/json’,
‘Authorization’: ‘<ваш_токен>’ // зависит от типа авторизации
},
body: JSON.stringify({ // здесь передаем тело запроса
key_1: value_1,
key_2: value_2
})
})
.then(res => {
console.log(‘Status:’, res.status);
return res.json();
})
.then(data => console.log(‘Body:’, data))
.catch(err => console.error(‘Ошибка:’, err));

Скопируй, подставь свои значения (URL, Headers и Body), вставь в консоль DevTools (F12 → Console) и нажми Enter. Ты увидишь что‑то вроде:
Status: 201
Body: <здесь ответ сервера>

Fetch для QA: как тестировать API прямо в DevTools, не открывая Postman - 3

Готово. Ты отправил POST‑запрос и получил ответ без Postman.

Про токен и тип авторизации:
В шаблоне стоит 'Authorization': '<ваш_токен>'. Тип зависит от того, какой тип авторизации.

Пару примеров типов:

Тип

Как выглядит

Basic Auth

Authorization: Basic <логин:пароль в base64>

Bearer Token

Authorization: Bearer <токен>

API Key

X‑API‑Key: <ключ>

Как узнать, какой у тебя: Посмотреть в доке или в DevTools → Network → любой запрос фронтенда → Request Headers. Смотри, что стоит рядом с Authorization или похожим заголовком. Если метод публичный и авторизация не нужна, то строку с Authorization можно не передавать.

А где вообще запускать fetch запрос? Во первых в консоле девтулза и во вторых запускать можно на любой странице. Fetch всё равно с какой страницы шлешь запрос. Но есть нюанс — CORS. Если ты на одном сайте, а запрос шлёшь на другой, браузер может заблокировать ответ. Увидишь ошибку вроде:

Access to fetch at '<URL_вашего_эндпоинта>'
from origin '
https://google.com' has been blocked by CORS policy

Поэтому желательно придерживать правила: открывай Console на том же домене, к которому идут запросы. Тестируешь test.com — сиди на test.com.

Разберём по строчкам шаблон fetch запроса

fetch('<URL_твоего_эндпоинта>', {аргумент №2})

Первый аргумент — это адрес куда шлем запрос (в формате строки). Второй — настройки. Если не указывать второй аргумент, то fetch отправит GET запрос.

method: 'POST'

Тип запроса. Если не указать method, то fetch отправит GET. Здесь мы явно указываем POST.

headers: { ... }

Набор заголовков зависит от того, как настроен API в твоём проекте. Host и Content‑Length можно не передавать. Но эти два заголовка нужно передать:

Заголовок

Обязателен?

Content‑Type: application/json

Да, если в методе передаем тело

Authorization: <тип> <токен>

Да, если метод приватный

body: JSON.stringify({...})

Тело запроса. JSON.stringify превращает объект в строку — HTTP передаёт только текст.

.then(res => ...)

Сюда попадает объект Response. У него есть:

  • res.status — код ответа (201, 404, 500)

  • res.ok — true, если статус в диапазоне 200–299

return res.json()

Тело ответа приходит строкой. res.json() парсит эту строку и превращает в объект — с ним уже можно работать: data.id, data.name

.then(data => ...)

Здесь data — готовый объект с ответом. Выводим его в консоль.

.catch(err => ...)

Ловим сетевые ошибки: нет интернета, сервер недоступен и тд. Помнишь из блока 2? Ошибки 4xx и 5xx сюда не попадают, так как fetch считает их успешными.

Как адаптировать под другие эндпоинты? Меняешь три вещи:

Что меняешь

Пример

URL

'<URL_вашего_эндпоинта>' → адрес твоего API

method

'POST' → 'PUT', 'PATCH' или 'DELETE' (и так далее)

body

Свое тело: { name: '...', email: '...' }

Как ранее писал, для GET‑запроса второй аргумент не нужен, но если нужна авторизация, заголовок остаётся:

fetch('<URL_вашего_эндпоинта>', {
headers: {
'Authorization': 'Bearer <ваш_токен>'
  }
})
  .then(res => res.json())
  .then(data => console.log(data));

Практика: находим запрос на реальном сайте

Для практики возьмём любой сайт, где есть формы или кнопки, создающие сущности. Я выбрал тренажёр для QA. Доступа к документации API у меня нет. Поэтому сначала выполню действие в приложении, а потом соберу fetch‑запрос по тому, что увижу в DevTools.

Если у тебя есть доступ к Swagger, то можешь собрать fetch по шаблону, взяв данные из доки. А если доступа нет, выручит Network в DevTools.

Шаг 1. Открой DevTools

Заходи на выбранный сайт. Открой DevTools — F12 или Ctrl+Shift+I (на Mac — Cmd+Option+I). Перейди на вкладку Network.

Ещё нажми на иконку «перечёркнутый круг» (Clear), чтобы очистить логи от лишнего.

Fetch для QA: как тестировать API прямо в DevTools, не открывая Postman - 4

Шаг 2. Сделай действие, которое создаёт сущность

Выполни любое действие, которые триггерит POST запрос. Например:

  • добавь нового пользователя

  • создай задачу

  • отправь сообщение

Теперь найди запрос в Network.

Fetch для QA: как тестировать API прямо в DevTools, не открывая Postman - 5

Шаг 3. Найди POST‑запрос и скопируй его fetch

В Network появится одна или несколько строк. Тебе нужна та, где в столбце Method стоит POST. Кликни по ней правой кнопкой мыши → Copy → Copy as fetch.

Fetch для QA: как тестировать API прямо в DevTools, не открывая Postman - 6

Всё. DevTools сам сгенерировал готовый fetch‑запрос. С URL, заголовками и телом. Осталось вставить его в Console.

Шаг 4. Вставь в Console и запусти

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

fetch("https://site.com/api/users", {
  "headers": {
    "accept": "application/json, text/plain, /",
    "authorization": "Bearer eyJhbGciOi...",
    "content-type": "application/json"
  },
  "body": "{"name":"Test User","email":"test@example.com"}",
  "method": "POST"
});

Нажми Enter и запрос отправляется. Для примера можешь зайти во вкладку Network и проверить этот запрос.

Шаг 5. Добавь вывод ответа

Скопированный запрос не выводит ответ. Он просто отправляет данные и всё. Ты не увидишь ни статус‑код, ни тело ответа. Поэтому допиши в конец цепочку и не забудь обновить на уникальные тестовые данные в теле:

fetch("https://site.com/api/users", {
  "headers": {
    "accept": "application/json, text/plain, /",
    "authorization": "Bearer eyJhbGciOi...",
    "content-type": "application/json"
  },
  "body": "{"name":"Test User","email":"test@example.com"}",
  "method": "POST"
})
  .then(res => {
    console.log('Status:', res.status);
    return res.json();
  })
  .then(data => console.log('Body:', data))
  .catch(err => console.error('Ошибка:', err));

Вот теперь в консоли появятся Status: 201 и Body: {...}. Всё как в блоке 3. Шаблон тот же, просто данные DevTools подставил сам.

Fetch для QA: как тестировать API прямо в DevTools, не открывая Postman - 7

Зачем это нужно на практике? Дальше ты можешь:

  • Проверить валидацию. Убери из body обязательное поле — увидишь, вернёт ли сервер 400 и понятное сообщение.

  • Проверить граничные значения. Отправь строку 101 символов туда, где ожидается 100 — сервер должен вернуть ошибку.

  • Проверить авторизацию

  • Повторить запрос 50 раз

  • и тд

Что дальше. Ты умеешь находить запрос в Network и отправлять его через fetch. Дальше как отправлять несколько запросов подряд, когда второй зависит от первого.

Запрос А → запрос Б

Классический пример: создаёшь сущность, получаешь её id, используешь этот id в следующем запросе. Пример: создаём сущность и проверяем, что она создалась.

Предположим, что есть два эндпоинта:

  • POST /api/entities — создаёт сущность, возвращает её id

  • GET /api/entities/{id} — возвращает сущность по id

Задача: создать сущность и убедиться, что запись сохранилась.

Первый запрос — создаём сущность:

fetch('https://site.com/api/entities', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Basic <ваш_токен>'
  },
  body: JSON.stringify({
    key_1: value_1,
    key_2: value_2
  })
})
  .then(res => res.json())
  .then(data => console.log('Создана:', data))
  .catch(err => console.error('Ошибка:', err));

В ответе придёт что‑то вроде:

Создана: {id: 47, key_1: "value_1", key_2: "value_2", ...}

Вот этот id: 47 нам и нужен для второго запроса.

Тут возникает вопрос: как передать id из первого запроса во второй? В примере ключ называется id, но у другого API он может называться иначе: uuid, key, entityId. Смотри, что приходит в ответе. Можно написать так, чтобы id подставлялся автоматически. Через await:

// Создаём сущность
const createRes = await fetch('https://site.com/api/entities', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Basic <ваш_токен>'
  },
  body: JSON.stringify({
    key_1: value_1,
    key_2: value_2
  })
});
const created = await createRes.json(); // тело ответа в created — здесь лежит id (или uuid, key)
console.log('Создана:', created);

// Запрашиваем её же по её id
const getRes = await fetch(
  https://site.com/api/entities/${created.id},
  {
    headers: {
      'Authorization': 'Basic <ваш_токен>'
    }
  }
);
const found = await getRes.json();
console.log('Найдена:', found);

Fetch для QA: как тестировать API прямо в DevTools, не открывая Postman - 8

Почему тут await, а не.then(). Fetch возвращает Promise. В блоках 3 и 4 мы ждали его через.then(). Это классический способ. Но когда запросов несколько и они зависят друг от друга,.then() становится громоздким: код растёт и читать становится тяжело. А await позволяет писать код сверху вниз, как обычные инструкции:

const created = await createRes.json();
// используем created.id в следующем запросе

Читается проще. Особенно когда запросов не два, а пять или десять. В блоке 6 это пригодится.

Массовая отправка запросов через fetch

Сделаем это через цикл:

const TOKEN = 'Basic <ваш_токен>';
const URL = 'https://site.com/api/entities';

for (let i = 1; i <= 50; i++) {
  const res = await fetch(URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': TOKEN
    },
    body: JSON.stringify({
      name: Сущность ${i},
      description: Описание номер ${i},
    })
  });
  const data = await res.json();
  console.log(Запрос ${i}:, res.status, data);
  await new Promise(resolve => setTimeout(resolve, 1000));
}

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

Запрос 1: 201 {id: 51, name: "Сущность 1", ...}
Запрос 2: 201 {id: 52, name: "Сущность 2", ...}
Запрос 3: 201 {id: 53, name: "Сущность 3", ...}
...
Запрос 50: 201 {id: 101, name: "Сущность 50", ...}

Fetch для QA: как тестировать API прямо в DevTools, не открывая Postman - 9

Разберём по строчкам простыми словами.

const TOKEN = ... и const URL = ...

Выносим повторяющиеся значения в переменные. Так код короче, и менять в одном месте значение быстрее.

for (let i = 1; i <= 50; i++)

Fetch для QA: как тестировать API прямо в DevTools, не открывая Postman - 10

Цикл, который выполнится 50 раз. i — это счётчик: начинается с 1, увеличивается на 1 на каждом шаге, останавливается на 50. Внутри цикла код, который отправит запрос. То есть 50 итераций = 50 запросов.

name: Сущность ${i}

Подставляет число i в название. Получаются: при первой итерации = «Сущность 1», при второй итерации = «Сущность 2» и так далее. Так каждая запись уникальна, иначе сервер вернет ошибку..

description: Описание номер ${i}

Здесь аналогично как для ключа name

await fetch(...)

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

console.log(Запрос ${i}:, res.status, data)

Выводим номер запроса, статус‑код и тело ответа. Сразу видно какой запрос упал.

await new Promise(resolve => setTimeout(resolve, 1000))

Пауза на 1 секунду между запросами. Строка делает простую вещь: ждёт 1000 миллисекунд перед следующим запросом.

Можно было бы просто написать «готово» в конце. Но тогда ты не поймёшь, что произошло, если где‑то ошибка.

С выводом каждого ответа видно:

  • Все 50 строк зелёные — всё хорошо.

  • На 23-й итерации 500 — сущность не создалась.

  • Скрипт завис на 30-й — возможно, сервер тормозит или отвалился.

Один момент

Если запустишь цикл на 50 запросов без остановки, то он выполнится за минуту. Но если что‑то пойдёт не так, остановить его сложно: Console не всегда прерывает await‑цикл по Esc. Поэтому на первых прогонах ставь небольшое число — 5 или 10. Убедись, что всё работает, потом увеличивай до 50+.

Параллельная отправка запросов через fetch

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

В JavaScript есть Promise.all. Он запускает несколько промисов сразу и ждёт, пока все завершатся.

const TOKEN = 'Basic <ваш_токен>';
const URL = 'https://site.com/api/entities';
const res1 = fetch(URL, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': TOKEN // Передаешь токен Пользователя №1
  },
  body: JSON.stringify({
    name: 'Пользователь 1',
    description: 'Создано первым'
  })
});
const res2 = fetch(URL, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': TOKEN // Передаешь токен Пользователя №2
  },
  body: JSON.stringify({
    name: 'Пользователь 2',
    description: 'Создано вторым'
  })
});
const [r1, r2] = await Promise.all([res1, res2]);
console.log('Первый:', r1.status, await r1.json());
console.log('Второй:', r2.status, await r2.json());

Что тут происходит:

const res1 = fetch(...)

Запускаем первый запрос и кладём промис в res1. Обрати внимание: без await. Мы не ждём результат — просто отправляем запрос и идём дальше.

const res2 = fetch(...)

То же самое для второго запроса. Оба уходят почти одновременно.

await Promise.all([res1, res2])

Ждём оба запроса. Promise.all берёт массив промисов и возвращает массив результатов, когда все они завершатся.

const [r1, r2] = ...

Разбираем массив на две переменные. r1 — ответ первого запроса, r2 — второго.

Зачем это нужно на практике? Представь: два пользователя одновременно нажимают «Создать». Или один и тот же пользователь отправляет форму дважды и кликнул два раза.

Проверить такие сценарии вручную сложно. А с Promise.all это сделать легко. Ты воспроизводишь скрипт и смотришь, как поведёт себя система.

Итог

Мы прошли путь от одного запроса до последовательной и параллельной отправки. Давай соберём всё вместе.

Что ты теперь знаешь

  • Отправлять один запрос через консоль девтулза

  • Копировать запрос из DevTools — через Copy as fetch

  • Связывать два запроса, когда второй зависит от первого.

  • Отправлять много запросов через цикл.

  • Отправлять два запроса одновременно через Promise.all.

Fetch в DevTools — не является полной заменой Postman. Это быстрый инструмент под рукой. Когда нужно проверить что‑то здесь и сейчас, не открывая лишних окон.

Автор: rgn23

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js