# Скрипты и тесты (/ru/docs/requests/scripts-and-tests)

Тесты показывают, соответствует ли ответ вашим ожиданиям. Echolot предлагает два способа их писать: проверки, которые вы
настраиваете на вкладке **Тесты** запроса без кода, и скрипты на JavaScript, которые выполняются перед отправкой запроса
и после получения ответа. Оба выполняются после каждой отправки, в [Прогоне](/docs/requests/runner) и в командной строке.

## Проверки на вкладке «Тесты» [#checks-in-the-tests-tab]

<Shot id="requests-tests-tab" alt="Вкладка «Тесты» запроса List products с четырьмя проверками, у каждой зелёная галочка: статус равен 200, значение JSON $.items.length больше 0, заголовок Content-Type содержит json, время ответа меньше 1000" />

1. Откройте запрос, например **Catalog › List products** в рабочем пространстве Alpenglow.
2. Выберите вкладку **Тесты**.
3. Выберите **Добавить тест** и укажите, что проверять.
4. Выберите сравнение в меню рядом и введите ожидаемое значение.
5. Отправьте запрос. Каждая проверка показывает зелёную галочку или красный крестик, а не прошедшая объясняет почему
   под собой.

Проверки сохраняются вместе с запросом и выполняются для каждого ответа с этого момента. Флажок в начале строки
выключает проверку, не удаляя её; кнопка «минус» удаляет. Значок вкладки «Тесты» считает проверки, а внизу вкладки
написано, сколько прошло, например «4 пройдено» или «1 из 4 с ошибкой».

Ожидаемое значение — это шаблон, поэтому в нём работают `{{variables}}`: **Catalog › Get product** проверяет, что
`$.sku` равно `{{productId}}`.

### Что можно проверять [#what-you-can-check]

| Проверка | Что читает | Сравнения |
| --- | --- | --- |
| **Статус** | Код состояния | равно, не равно, меньше, больше, одно из |
| **Заголовок** | Заголовок ответа по имени | существует, отсутствует, равно, не равно, содержит, не содержит, соответствует regex |
| **Значение JSON** | Значение по JSONPath в теле JSON | существует, отсутствует, равно, не равно, содержит, не содержит, меньше, не больше, больше, не меньше, соответствует regex, имеет тип, одно из |
| **Тело** | Всё тело как текст | содержит, не содержит, равно, соответствует regex |
| **Время ответа (мс)** | Общее время обмена в миллисекундах | меньше, не больше, больше, не меньше |
| **Размер (байты)** | Размер тела в байтах | меньше, не больше, больше, не меньше |
| **JSON Schema** | Тело по JSON Schema, которую вы вставляете | соответствует |
| **Ошибки GraphQL** | Массив `errors` ответа GraphQL | равно, меньше, больше (их число); содержит, не содержит (их сообщения) |
| **Ошибка MCP** | Не завершился ли вызов MCP ошибкой: `true` или `false` | равно |
| **Схема вывода инструмента** | `structuredContent` результата инструмента MCP по собственной `outputSchema` инструмента | соответствует |
| **Спецификация OpenAPI** | Тело по схеме привязанной спецификации OpenAPI для статуса ответа | соответствует |
| **Статус gRPC** | Статус вызова gRPC по имени или коду | равно, не равно, одно из |

Новая проверка начинается с разумного значения: **Статус** равно `200`, **Время ответа (мс)** меньше `1000`,
**Ошибки GraphQL** равно `0`, **Ошибка MCP** равно `false`, **Статус gRPC** равно `OK`, а **JSON Schema** — с
`{"type": "object"}`.

### Как сравниваются значения [#how-values-compare]

- **равно** сравнивает числа как числа, так что `200` равно `200.0`, и игнорирует пробелы вокруг значения.
- **одно из** принимает список через запятую: `200, 201, 204`.
- **соответствует regex** проходит, если регулярное выражение совпадает где угодно в значении.
- **имеет тип** принимает `object`, `array`, `string`, `number`, `integer`, `boolean` или `null`.
- Числовые сравнения требуют чисел с обеих сторон; всё остальное не проходит с сообщением об этом.

### Значения JSON [#json-values]

Путь — это JSONPath из ключей и индексов, начинающийся с `$`: `$.items[0].sku`, `$.data.search.totalCount`.
Отрицательный индекс считается с конца: `$.items[-1]`. Добавьте `.length`, чтобы посчитать элементы массива или ключи
объекта: `$.items.length` больше `0`. Echolot читает только ту часть тела, что лежит на пути к значению, поэтому
проверки JSON работают с телами любого размера.

**Тело** с **содержит** или **не содержит** тоже работает с телами любого размера. Остальные сравнения тела, а также
**JSON Schema** и **Ошибки GraphQL** читают всё тело и останавливаются на 50 МБ.

### Проверки для других видов запросов [#checks-for-other-kinds-of-request]

- **GraphQL**: серверы отвечают ошибками со статусом 200, поэтому добавьте **Ошибки GraphQL** равно `0`, чтобы их ловить.
  Используйте **содержит**, чтобы проверить, что приходит определённое сообщение.
- **Вызовы MCP**: **Ошибка MCP** равно `false` проходит, если вызов удался. Проверка не проходит на результате,
  помеченном как ошибка, и на ошибке JSON-RPC, а сообщение показывает, что сказал сервер. **Схема вывода инструмента**
  проверяет результат инструмента по схеме, которую он объявляет; проверка не проходит, если инструмент её не
  объявляет. См. [Запросы MCP](/docs/requests/mcp-requests).
- **gRPC**: **Статус gRPC** принимает имена вроде `OK` и `NOT_FOUND` или коды от 0 до 16; `5`, `not_found` и
  `NOT_FOUND` — один и тот же статус. Не прошедшая проверка показывает сообщение статуса. См.
  [gRPC](/docs/requests/grpc).
- **Спецификация OpenAPI**: папка запроса (или папка выше) должна быть привязана к спецификации, а запрос должен
  следовать одной из её операций. Тогда проверка сверяет тело со схемой, которую спецификация документирует для
  пришедшего статуса. Статус, который спецификация документирует без тела JSON, проходит; недокументированный статус не
  проходит. См. [Спецификации](/docs/specs).

### Добавление проверок из ответа [#add-checks-from-a-response]

Вводить каждую проверку не обязательно:

- **Создать из ответа…** на вкладке «Тесты» предлагает проверки по последнему ответу. Проверки, которые выполняются для
  любого хорошего ответа, — статус, тип содержимого и форма тела в виде JSON Schema — уже отмечены. Значения,
  которые меняются от вызова к вызову, и время предлагаются, но не отмечены. Выберите **выведенная схема**, чтобы
  прочитать предложенную схему. Выберите **Добавить 1 тест** (или «Добавить N тестов»), чтобы оставить отмеченные.
  Сначала отправьте запрос: кнопке нужен ответ.
- В **Тело** ответа в виде JSON-дерева щёлкните значение правой кнопкой и выберите **Добавить проверку**: она добавит
  проверку **Значение JSON**, что путь равен этому значению.
- В **Заголовки** ответа щёлкните заголовок правой кнопкой и выберите **Добавить проверку**: она добавит проверку
  **Заголовок**, что заголовок равен этому значению.

<Shot id="requests-tests-generate" alt="Лист «Создать тесты из ответа» для List products: отмечены статус, тип содержимого и выведенная схема, а проверка значения предложена, но не отмечена" />

### Результаты [#results]

Вкладка **Тесты** области ответа перечисляет каждую проверку с её итогом и найденным значением (`got 200`), затем каждый
тест из скриптов. Страница **Тесты** в боковой панели (**Вид › Тесты**, <kbd>⌥</kbd><kbd>⌘</kbd><kbd>T</kbd>) перечисляет
все запросы, у которых есть проверки, и то, как они выполняются в последнем ответе; нажмите один, чтобы открыть запрос.

## Скрипты [#scripts]

Скрипты — это JavaScript с API `pm.*` из Postman, так что большинство скриптов Postman работает без изменений. У запроса
может быть два:

- Скрипт **перед запросом** выполняется до отправки запроса. Он может изменить запрос, задать переменные или пропустить
  запрос.
- **Тестовый** скрипт выполняется после получения ответа. Он проверяет ответ через `pm.test` и может сохранять значения
  для следующих запросов.

<Shot id="requests-scripts-tab" alt="Вкладка «Скрипты» запроса List products на «Тесты» с pm.test, проверяющим статус и число элементов, и открытым меню «Фрагменты»" />

1. Откройте запрос и выберите вкладку **Скрипты**.
2. Выберите **Перед запросом** или **Тесты** вверху. Точка после названия, например **Тесты •**, отмечает скрипт, в
   котором есть код.
3. Напишите скрипт. Редактор подсвечивает JavaScript и дополняет пути `pm.` по мере ввода. **Фрагменты** добавляют
   готовый скрипт для типичных случаев.
4. Отправьте запрос.

Например, тестовый скрипт для **Catalog › List products**:

```js
pm.test("Status code is 200", () => {
  pm.response.to.have.status(200);
});

pm.test("Returns tents", () => {
  const body = pm.response.json();
  pm.expect(body.items.length).to.be.above(0);
});
```

И скрипт перед запросом, добавляющий заголовок к каждому запросу, для которого он выполняется:

```js
pm.request.headers.add({ key: "X-Request-Id", value: pm.variables.replaceIn("{{$guid}}") });
```

Каждый `pm.test` появляется на вкладке **Тесты** ответа в разделе **Скрипты** после проверок с вкладки «Тесты». Скрипт,
который выбрасывает исключение, показывается как **Ошибка скрипта** с сообщением. То, что скрипты пишут через
`console.log`, `console.info`, `console.warn` и `console.error`, появляется на вкладке **Консоль** ответа с пометкой
`pre` или `test`; вкладка появляется, когда скрипты выполнились.

### Фрагменты [#snippets]

| Фрагмент | Для чего |
| --- | --- |
| **Код состояния — 200**, **Время ответа меньше 500 мс**, **Значение JSON существует**, **Заголовок присутствует**, **Тело содержит текст** | Тесты |
| **Сохранить значение для следующих запросов** | Тесты: `pm.environment.set("token", pm.response.json().token)` |
| **Запустить следующим другой запрос** | Тесты: `pm.execution.setNextRequest("Request name")` |
| **Задать переменную**, **Добавить заголовок**, **Подписать запрос HMAC-SHA256**, **Сначала получите токен**, **Пропустить этот запрос** | Перед запросом |

### Скрипты на папках и рабочем пространстве [#scripts-on-folders-and-the-workspace]

Скрипты могут быть и у папки, и у рабочего пространства — для каждого запроса внутри них. Откройте их из контекстного
меню папки в боковой панели (**Настройки папки…**) или из **Настройки рабочего пространства…** на вкладке **Скрипты**.

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

У вызовов MCP нет вкладки «Скрипты», и они не выполняют скриптов. У любого другого вида запросов она есть.

### API `pm` вкратце [#the-pm-api-at-a-glance]

Полный список — в [справочнике по API скриптов](/docs/reference/scripting-api).

| Объект | Что делает |
| --- | --- |
| `pm.test(name, fn)`, `pm.expect(value)` | Именованный тест и утверждения в стиле Chai: `pm.expect(x).to.equal(1)` |
| `pm.response` | Ответ в тестовых скриптах: `code`, `status`, `headers`, `responseTime`, `responseSize`, `json()`, `text()`, а также `pm.response.to.have.status(200)`, `.header()`, `.body()`, `.jsonBody()`, `pm.response.to.be.ok`, `.success`, `.json`, `.clientError`, `.serverError`, `.notFound` |
| `pm.request` | Запрос: `url`, `method`, `headers`, `body`; скрипт перед запросом может добавлять, заменять и удалять заголовки и параметры запроса |
| `pm.variables` | Все области, ближайшая первой: `get`, `set`, `has`, `unset`, `replaceIn`, `toObject` |
| `pm.environment`, `pm.collectionVariables`, `pm.globals` | Активное окружение, переменные рабочего пространства и глобальные переменные |
| `pm.iterationData` | Строка данных прогона: `get`, `has`, `toObject` |
| `pm.info` | `requestName`, `requestId`, `iteration` (с 0), `iterationCount`, `eventName` |
| `pm.execution` | `setNextRequest(name)` (`null` завершает прогон) и `skipRequest()` |
| `pm.cookies` | Cookie ответа |
| `pm.sendRequest` | Отправляет другой запрос |
| `console` | `log`, `info`, `warn`, `error` |
| `CryptoJS` | `MD5`, `SHA1`, `SHA256`, `SHA512`, `HmacSHA1`, `HmacSHA256`, `HmacSHA512` и `CryptoJS.enc` (`Hex`, `Base64`, `Utf8`, `Latin1`) |

`require` даёт `lodash`, `ajv`, `tv4`, `crypto-js`, `uuid`, `atob` и `btoa`. `_` (lodash), `tv4`, `btoa`, `atob` и
`setTimeout` доступны и без него. Старые глобальные объекты Postman (`postman.setEnvironmentVariable`, `tests[...]`,
`environment`, `globals`, `data`, `iteration`) по-прежнему работают. Любой другой модуль завершается ошибкой: модуль
недоступен в скриптах Echolot.

### Переменные, которые задают скрипты [#variables-that-scripts-set]

То, что записывают `pm.environment.set`, `pm.collectionVariables.set` и `pm.globals.set`, — текущее значение: оно живёт,
пока открыто окно, при разрешении запросов перекрывает сохранённое значение своей области и никогда не записывается в
файлы рабочего пространства. Поэтому токен, который сохраняет скрипт, доходит до следующих запросов и не попадает в Git.
Чтобы сохранить значение из ответа в рабочем пространстве, используйте вместо этого [захват](/docs/requests/captures).

`pm.variables.set` задаёт значение только для текущего прогона: прогон начинается без значений, заданных так скриптами.

### Отправка другого запроса [#sending-another-request]

`pm.sendRequest` отправляет запрос из скрипта, например чтобы сначала получить токен. Он принимает URL или объект с
`url`, `method`, `header` и `body`, а также функцию обратного вызова или возвращаемый им promise:

```js
pm.sendRequest({
  url: pm.variables.replaceIn("{{baseURL}}/auth/token"),
  method: "POST",
  header: { "Content-Type": "application/json" },
  body: { mode: "raw", raw: JSON.stringify({ email: "heidi@alpenglow.ch", password: "alpenglow" }) },
}, (error, response) => {
  if (!error) pm.environment.set("token", response.json().access_token);
});
```

Запрос уходит так же, как любая другая отправка из Echolot, с теми же прокси и сертификатами, но без авторизации
рабочего пространства или папки. Скрипты ждут ответы до 30 секунд.

### Пропуск запросов и выбор следующего [#skipping-requests-and-choosing-the-next-one]

`pm.execution.skipRequest()` в скрипте перед запросом не даёт запросу отправиться; область ответа и Прогон сообщают, что
он пропущен. `pm.execution.setNextRequest("Get order")` в прогоне переходит сразу к этому запросу, а
`setNextRequest(null)` завершает прогон. См. [Прогон](/docs/requests/runner).

## Скрипты не из Echolot [#scripts-from-outside-echolot]

Скрипты — это код, поэтому Echolot выполняет только те, которым вы доверяете на этом Mac. Скрипты, написанные в Echolot,
считаются доверенными в момент сохранения. Скрипты, которые попали иначе — из импорта, из Git или после правки в другом
редакторе, — ждут:

- Вкладка «Скрипты» показывает **Эти скрипты пришли не из Echolot** и **Доверять скриптам**.
- Отправка запроса останавливается с **Просмотреть скрипты…**. Открывается **Запустить эти скрипты?** с каждым ещё не
  выполнявшимся скриптом и указанием, откуда он (запрос, папка или рабочее пространство). Прочитайте их, затем выберите
  **Доверять и отправить** или **Отменить**.

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

Инструмент командной строки выполняет все скрипты указанного вами рабочего пространства. `echolot mcp serve` выполняет
только те скрипты, которые были в рабочем пространстве при его запуске.

## Песочница и её ограничения [#the-sandbox-and-its-limits]

Скрипты выполняются в песочнице JavaScript внутри Echolot. У них нет файловой системы, собственной сети и доступа к Mac:
единственные выходы наружу — `pm.sendRequest`, `console` и переменные. Каждая отправка начинается с новой песочницы.

| Ограничение | Значение |
| --- | --- |
| Время JavaScript на все скрипты одного события (перед запросом или тест) | 5 секунд |
| Память | 128 МБ |
| Ожидание ответов `pm.sendRequest` | 30 секунд |
| Тело ответа, которое видит скрипт | Первые 32 МБ |

Скрипт, который выполняется слишком долго, завершается с ошибкой «the script took longer than 5 s», а использующий слишком
много памяти — с «the script used more memory than allowed». Скрипты по-прежнему могут читать переменные рабочего
пространства, включая секреты, и отправлять их через `pm.sendRequest`, поэтому скрипты со стороны ждут вашего доверия.

## Если что-то не работает [#troubleshooting]

- **Создать из ответа… неактивна**: сначала отправьте запрос. Она работает для запросов HTTP и GraphQL.
- **«к папке этого запроса не привязана спецификация OpenAPI»**: привяжите папку к спецификации или удалите проверку
  **Спецификация OpenAPI**.
- **«“…” — JSONPath, который эта версия не понимает»**: используйте только ключи и индексы, например `$.items[0].sku`.
- **Скрипт не выполняется**: проверьте, что запрос сохранён, что это не вызов MCP и что скрипт не ждёт вашего доверия.
