# スクリプトとテスト (/ja/docs/requests/scripts-and-tests)

テストは、レスポンスが期待どおりかどうかを教えてくれます。Echolotでは、テストの書き方が2つあります。リクエストの**テスト**タブで設定するコード不要のチェックと、
リクエストの送信前と応答後に実行されるJavaScriptのスクリプトです。どちらも送信のたびに、[ランナー](/docs/requests/runner)やコマンドラインでも実行されます。

## テストタブのチェック [#checks-in-the-tests-tab]

<Shot id="requests-tests-tab" alt="List productsのテストタブ。4つのチェックがそれぞれ緑のチェックマーク付き：ステータスが200と等しい、JSONの値$.items.lengthが0より大きい、ヘッダーContent-Typeがjsonを含む、レスポンス時間が1000未満" />

1. リクエストを開きます。たとえば、Alpenglowワークスペースの**Catalog › List products**です。
2. **テスト**タブを選びます。
3. **テストを追加**を選び、チェックする対象を選びます。
4. 横のメニューから比較方法を選び、期待する値を入力します。
5. リクエストを送信します。各チェックには緑のチェックマークか赤の×が表示され、失敗したものには下にその理由が表示されます。

チェックはリクエストとともに保存され、以降のすべてのレスポンスに対して実行されます。行の先頭のボックスは、チェックを削除せずにオフにします。マイナスボタンで削除します。
テストタブのバッジにはチェックの数が表示され、タブの下部には**4件成功**や**4件中1件が失敗**のように、合格した数が表示されます。

期待値はテンプレートなので、`{{variables}}`が使えます。**Catalog › Get product**は、`$.sku`が`{{productId}}`と等しいことをチェックします。

### チェックできるもの [#what-you-can-check]

| チェック | 読み取る対象 | 比較 |
| --- | --- | --- |
| **ステータス** | ステータスコード | 次と等しい、次と等しくない、次より小さい、次より大きい、次のいずれか |
| **ヘッダー** | 名前で指定したレスポンスヘッダー | 存在する、存在しない、次と等しい、次と等しくない、次を含む、次を含まない、次の正規表現に一致 |
| **JSONの値** | JSONボディ内のJSONPathの値 | 存在する、存在しない、次と等しい、次と等しくない、次を含む、次を含まない、次より小さい、次以下、次より大きい、次以上、次の正規表現に一致、次の型である、次のいずれか |
| **ボディ** | テキストとしてのボディ全体 | 次を含む、次を含まない、次と等しい、次の正規表現に一致 |
| **レスポンス時間（ms）** | やり取りの合計時間（ミリ秒） | 次より小さい、次以下、次より大きい、次以上 |
| **サイズ（バイト）** | ボディのバイト数 | 次より小さい、次以下、次より大きい、次以上 |
| **JSON Schema** | 貼り付けたJSON Schemaに対するボディ | 次に一致 |
| **GraphQLエラー** | GraphQLレスポンスの`errors`配列 | 次と等しい、次より小さい、次より大きい（件数）、次を含む、次を含まない（メッセージ） |
| **MCPエラー** | MCP呼び出しが失敗したかどうか：`true`または`false` | 次と等しい |
| **ツールの出力スキーマ** | MCPツールの結果の`structuredContent`を、ツール自身の`outputSchema`に対して | 次に一致 |
| **OpenAPI仕様** | リンクされたOpenAPI仕様の、レスポンスのステータスに対するスキーマに対するボディ | 次に一致 |
| **gRPCステータス** | gRPC呼び出しのステータス（名前またはコード） | 次と等しい、次と等しくない、次のいずれか |

新しいチェックは、妥当な値で始まります：<strong>ステータス</strong>は`200`と等しい、<strong>レスポンス時間（ms）</strong>は`1000`より小さい、<strong>GraphQLエラー</strong>は`0`と等しい、<strong>MCPエラー</strong>は`false`と等しい、
**gRPCステータス**は`OK`と等しい、**JSON Schema**は`{"type": "object"}`です。

### 値の比較方法 [#how-values-compare]

- **次と等しい**は、数値を数値として比較するので、`200`は`200.0`と等しく、値の前後のスペースは無視されます。
- **次のいずれか**には、`200, 201, 204`のようにカンマ区切りのリストを指定します。
- **次の正規表現に一致**は、正規表現が値のどこかに一致すれば合格します。
- **次の型である**には、`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 MBで止まります。

### ほかの種類のリクエストのチェック [#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]

スクリプトは、Postmanの`pm.*` APIを使うJavaScriptなので、ほとんどのPostmanスクリプトはそのまま動作します。リクエストには2つのスクリプトを持てます：

- **プリリクエスト**スクリプトは、リクエストの送信前に実行されます。リクエストを変更したり、変数を設定したり、リクエストをスキップしたりできます。
- **テスト**スクリプトは、レスポンスが届いた後に実行されます。`pm.test`でレスポンスをチェックし、後のリクエストのために値を保存できます。

<Shot id="requests-scripts-tab" alt="List productsのテストのスクリプトタブ。ステータスと項目数をチェックするpm.testがあり、スニペットメニューが開いている" />

1. リクエストを開き、**スクリプト**タブを選びます。
2. 上部の<strong>プリリクエスト</strong>または<strong>テスト</strong>を選びます。<strong>テスト •</strong>のように名前の後にドットがあると、コードのあるスクリプトです。
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 ms未満**、**JSONの値がある**、**ヘッダーがある**、**ボディにテキストが含まれる** | テスト |
| **以降のリクエストのために値を保存** | テスト：`pm.environment.set("token", pm.response.json().token)` |
| **次に別のリクエストを実行** | テスト：`pm.execution.setNextRequest("Request name")` |
| **変数を設定**、**ヘッダーを追加**、**HMAC-SHA256でリクエストに署名**、**先にトークンを取得**、**このリクエストをスキップ** | プリリクエスト |

### フォルダとワークスペースのスクリプト [#scripts-on-folders-and-the-workspace]

フォルダとワークスペースにも、その中のすべてのリクエストに対するスクリプトを設定できます。サイドバーのフォルダのコンテキストメニュー（<strong>フォルダ設定…</strong>）か<strong>ワークスペース設定…</strong>から、<strong>スクリプト</strong>タブで開きます。

各リクエストでは、Echolotは最初にワークスペースのスクリプト、次に外側から順に各フォルダのスクリプト、最後にリクエスト自身のスクリプトを実行します。
プリリクエストスクリプトとテストスクリプトのどちらも、同じ順序です。スクリプトが実行されるのは、保存済みのリクエストだけです。

MCP呼び出しにはスクリプトタブがなく、スクリプトも実行されません。ほかのすべての種類のリクエストにはあります。

### `pm` APIの概要 [#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`）も、引き続き動作します。
それ以外のモジュールは、「The module … is not available in Echolot scripts.」というメッセージで失敗します。

### スクリプトが設定する変数 [#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]

スクリプトは、Echolot内のJavaScriptサンドボックスで実行されます。ファイルシステムも、独自のネットワークも、Macへのアクセスもありません。外へ出る手段は、`pm.sendRequest`、`console`、変数だけです。
送信のたびに、新しいサンドボックスが始まります。

| 制限 | 値 |
| --- | --- |
| 1つのイベント（プリリクエストまたはテスト）のすべてのスクリプトのJavaScript時間 | 5秒 |
| メモリ | 128 MB |
| `pm.sendRequest`の応答の待機 | 30秒 |
| スクリプトが見るレスポンスボディ | 最初の32 MB |

実行時間が長すぎるスクリプトは「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呼び出しでないこと、スクリプトが信頼を待っていないことを確認してください。
