🐙

Postman Collection Runner (負荷+ロングランテストツール)が失敗したテストの分析に対応たので試してみた

に公開

過去こちらの記事でPostmanを使った負荷試験のやり方をご紹介しました。
https://zenn.dev/kameoncloud/articles/c3b6f6a2ac3445
その当時はエラーが発生した統計は取れたのですが、個別エラーの詳細(レスポンス情報や複数エラーコードの分布分析)までは行えませんでした。

2025年12月19日にアップデートがあり、、失敗したテストアサーションを含むリクエストを表示できるようになりました。
https://www.postman.com/release-notes/postman-app/

現時点でこの機能はPostmanのデスクトップアプリでのみ動作します。もともとPostmanではデスクトップアプリの利用を推奨しています。

さっそくやってみる

まずエラーが安定して発生するテスト用APIを作成します。(変な日本語ですが、エラーが安定して発生してくれないとこの機能を試せません)

server.js
// server.js (ES Modules)
// 確率制御されたステータスコード返却サーバ
// 70%:200 / 10%:400 / 10%:429 / 10%:503
// 各リクエストで4桁乱数を発行し、エラー時はターミナルに通知

import http from "node:http";

const PORT = process.env.PORT ? Number(process.env.PORT) : 3000;

// ---- ユーティリティ ----

// 0000〜9999 の4桁乱数
function gen4digits() {
  return String(Math.floor(Math.random() * 10000)).padStart(4, "0");
}

// 確率に応じてHTTPステータスを選択
function pickStatus() {
  const r = Math.random();

  if (r < 0.7) return 200;   // 70%
  if (r < 0.8) return 400;   // 10%
  if (r < 0.9) return 429;   // 10%
  return 503;               // 10%
}

function readBody(req) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    let size = 0;

    req.on("data", (chunk) => {
      size += chunk.length;
      if (size > 1 * 1024 * 1024) {
        reject(new Error("Payload too large"));
        req.destroy();
        return;
      }
      chunks.push(chunk);
    });

    req.on("end", () => resolve(Buffer.concat(chunks)));
    req.on("error", reject);
  });
}

function sendJson(res, status, body) {
  res.writeHead(status, {
    "Content-Type": "application/json; charset=utf-8",
    "X-Request-Code": body.code, // 相関用
  });
  res.end(JSON.stringify(body));
}

// ---- サーバ ----

const server = http.createServer(async (req, res) => {
  const code = gen4digits(); // ★ リクエスト単位の識別子
  const start = Date.now();

  try {
    // health check
    if (req.method === "GET" && (req.url === "/" || req.url === "/health")) {
      sendJson(res, 200, { ok: true, code });
      return;
    }

    if (req.method !== "POST" || (req.url !== "/" && req.url !== "/post")) {
      sendJson(res, 404, {
        ok: false,
        code,
        error: "Not Found",
      });
      return;
    }

    const bodyBuf = await readBody(req);
    const bodyText = bodyBuf.toString("utf8");

    let body;
    try {
      body = bodyText ? JSON.parse(bodyText) : null;
    } catch {
      body = { _raw: bodyText, _note: "invalid json" };
    }

    // ★ 確率に応じたステータス決定
    const status = pickStatus();

    // ---- エラー系 ----
    if (status !== 200) {
      console.error(
        `[ERROR] status=${status} code=${code} ${req.method} ${req.url} bytes=${bodyBuf.length}`
      );

      sendJson(res, status, {
        ok: false,
        code,
        error:
          status === 400 ? "Bad Request" :
          status === 429 ? "Too Many Requests" :
          "Service Unavailable",
      });
      return;
    }

    // ---- 成功 ----
    sendJson(res, 200, {
      ok: true,
      code,
      received: body,
      receivedBytes: bodyBuf.length,
      ms: Date.now() - start,
    });

  } catch (err) {
    console.error(
      `[ERROR] status=500 code=${code} message=${err?.message || err}`
    );

    sendJson(res, 500, {
      ok: false,
      code,
      error: "Internal Server Error",
    });
  }
});

server.listen(PORT, () => {
  console.log(`Listening on http://localhost:${PORT}`);
  console.log(`POST http://localhost:${PORT}/post`);
  console.log("Distribution: 70% 200 / 10% 400 / 10% 429 / 10% 503");
});

node server.jsで起動します
このウェブサーバは以下の動作をします。

  1. 127.0.0.1:3000 でPOSTを待ち受ける
  2. Requestごとに4桁の数字を生成してResponseで戻す
  3. 30%のリクエストは失敗してそれぞれ以下の割合でHTTPステータスコードをエラーとして戻す。)
    (10% 400 / 10% 429 / 10% 503)

これにより今回のアップデートでエラーが発生した際のResponse値を確認できるようにします。

次にCollectionを作成します。

Collectionが格納されているフォルダを選択し実行をクリックします。

負荷のかけ方や仮想クライアント数、試験時間などを指定して実行をクリックします。

このようにテストの状況がグラフでリアルタイムで出力され始めます。

画面上部Error Rateをクリックします。

このように発生したエラーの分布が確認出ます。

Errorsタブをクリックするとエラーの詳細が確認できます。

画面下部ではエラーが発生したリクエスト単位でResponse,Header,Requestを確認できるようになっています。

Discussion