Postman の Pre-request(送信前)と Post-response(受信後)にスクリプトを書くと、トークンの自動更新やレスポンスの検証を手作業なしで回せます。
この記事は2024年の公開後、2026年9月に見直しました。 公開当時のコードには、Postman のサンドボックスでは動かないものが含まれていたため差し替えています。
jwt.sign()は動きません。jsonwebtokenは Postman の組み込みモジュールに含まれていませんrequire('crypto')も動きません。 使える Node.js モジュールはpath/buffer/util/url/querystring/streamなどに限られます- なお
require('uuid')は組み込みなので使えます
スポンサーリンク
Pre-request と Post-response の役割
| Pre-request | Post-response | |
|---|---|---|
| 実行タイミング | リクエスト送信前 | レスポンス受信後 |
| 主な用途 | 認証情報の準備、値の生成 | 検証、次のリクエストへの値の受け渡し |
| 典型例 | トークン取得、タイムスタンプ、署名 | ステータス確認、スキーマ検証 |
なお、かつて Tests と呼ばれていたタブが Post-response に名称変更されました。古い記事にある「Testsタブ」は、現在の Post-response のことです。
使えるモジュールを先に把握する
ここを知らないと時間を無駄にします。 Postman のスクリプトは Node.js そのものではなく、限定されたサンドボックスで動きます。
| 種類 | 使えるもの |
|---|---|
| npm パッケージ | ajv, chai, cheerio, csv-parse/lib/sync, lodash, moment, postman-collection, uuid, xml2js |
| Node.js モジュール | path, assert, buffer, util, url, punycode, querystring, string-decoder, stream, timers, events |
| それ以外 | pm.require('npm:パッケージ名@バージョン') で読み込む |
crypto、fs、jsonwebtoken は組み込みには含まれていません。 暗号処理が必要な場合は、外部パッケージとして読み込むか、グローバルの crypto オブジェクト(Web Crypto API)を使います。
スポンサーリンク
Pre-request の実用例
1. アクセストークンを自動で取り直す
これが最も効果の大きい使い方です。 「テストを流したらトークンが切れていた」という手戻りが無くなります。
const expiresAt = Number(pm.environment.get('token_expires_at') || 0);
const now = Date.now();
// 有効期限の60秒前を過ぎていたら取り直す
if (now < expiresAt - 60_000) {
return; // まだ有効。何もしない
}
pm.sendRequest({
url: pm.environment.get('base_url') + '/auth/token',
method: 'POST',
header: { 'Content-Type': 'application/json' },
body: {
mode: 'raw',
raw: JSON.stringify({
client_id: pm.environment.get('client_id'),
client_secret: pm.environment.get('client_secret'),
}),
},
}, (err, res) => {
if (err) {
console.error('トークン取得に失敗しました', err);
return;
}
if (res.code !== 200) {
console.error('トークン取得に失敗しました', res.code, res.text());
return;
}
const data = res.json();
pm.environment.set('access_token', data.access_token);
pm.environment.set('token_expires_at', Date.now() + data.expires_in * 1000);
});
ポイントは2つです。
- 毎回取り直さない:有効期限を保存しておき、切れる直前だけ再取得します。認証サーバーへの無駄な負荷を避けられます
errとres.codeの両方を確認する:pm.sendRequestは通信自体が失敗したときだけerrを返します。401 が返ってきた場合はerrはnullです
2. タイムスタンプと乱数を用意する
// UNIXタイムスタンプ(秒)
pm.environment.set('current_timestamp', Math.floor(Date.now() / 1000));
// UUID:uuid モジュールは組み込みなので使える
const { v4: uuidv4 } = require('uuid');
pm.environment.set('request_id', uuidv4());
// 組み込みの動的変数を使う方法もある
pm.environment.set('random_email', pm.variables.replaceIn('{{$randomEmail}}'));
pm.environment.set('random_name', pm.variables.replaceIn('{{$randomFullName}}'));
Math.random().toString(36).substring(7) は避けてください。 生成される文字列の長さが安定せず、まれに空文字になります。テストが不定期に落ちる原因になります。
動的変数({{$guid}} など)はリクエスト本文の中では直接使えますが、スクリプト内では pm.variables.replaceIn() を通さないと展開されません。ここも間違えやすい点です。
3. HMAC 署名を作る
API によっては、リクエストに署名を付ける必要があります。require('crypto') は使えないので、外部パッケージを読み込みます。
const CryptoJS = pm.require('npm:crypto-js@4.2.0');
const secret = pm.environment.get('api_secret');
const timestamp = Math.floor(Date.now() / 1000);
const method = pm.request.method;
const path = pm.request.url.getPath();
const message = `${method}\n${path}\n${timestamp}`;
const signature = CryptoJS.HmacSHA256(message, secret).toString(CryptoJS.enc.Hex);
pm.environment.set('timestamp', timestamp);
pm.environment.set('signature', signature);
JWT を自前で組み立てる場合も同じ考え方になります。jwt.sign() のような便利な関数は用意されていないので、ヘッダーとペイロードを Base64URL エンコードして連結し、HMAC を計算します。
const CryptoJS = pm.require('npm:crypto-js@4.2.0');
function base64url(source) {
return CryptoJS.enc.Base64.stringify(source)
.replace(/=+$/, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
}
const header = { alg: 'HS256', typ: 'JWT' };
const payload = {
sub: 'user@example.com',
name: 'John Doe',
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600,
};
const encodedHeader = base64url(CryptoJS.enc.Utf8.parse(JSON.stringify(header)));
const encodedPayload = base64url(CryptoJS.enc.Utf8.parse(JSON.stringify(payload)));
const secret = pm.environment.get('jwt_secret');
const signature = base64url(
CryptoJS.HmacSHA256(`${encodedHeader}.${encodedPayload}`, secret)
);
pm.environment.set('jwt_token', `${encodedHeader}.${encodedPayload}.${signature}`);
秘密鍵をスクリプトに直書きしないでください。 環境変数、それも Secret 型の変数に入れます。コレクションを共有したときに鍵が漏れます。
Post-response の実用例
1. 基本の検証
pm.test('ステータスコードが200である', () => {
pm.response.to.have.status(200);
});
pm.test('レスポンスが1秒以内に返る', () => {
pm.expect(pm.response.responseTime).to.be.below(1000);
});
pm.test('Content-Type が JSON である', () => {
pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json');
});
レスポンスタイムの閾値を厳しくしすぎないでください。 ネットワークの揺らぎで落ちるテストは、やがて誰も見なくなります。500ms より 1〜2秒程度の方が実用的です。
2. 中身の検証
pm.test('必要なプロパティが揃っている', () => {
const data = pm.response.json();
pm.expect(data).to.have.property('user_id');
pm.expect(data.user_id).to.be.a('number');
pm.expect(data).to.have.property('email');
pm.expect(data.email).to.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
});
存在確認だけでなく型も確認するのが要点です。user_id が数値のはずが文字列で返ってくる、といった不具合は to.have.property() だけでは検出できません。
3. JSON スキーマでまとめて検証する
項目が多い場合は、1つずつ書くよりスキーマで検証する方が保守しやすくなります。ajv が組み込みで使えます。
const Ajv = require('ajv');
const ajv = new Ajv();
const schema = {
type: 'object',
required: ['user_id', 'email', 'created_at'],
properties: {
user_id: { type: 'integer' },
email: { type: 'string', format: 'email' },
created_at: { type: 'string' },
tags: { type: 'array', items: { type: 'string' } },
},
};
pm.test('レスポンスがスキーマに一致する', () => {
const validate = ajv.compile(schema);
const valid = validate(pm.response.json());
if (!valid) console.error(validate.errors);
pm.expect(valid, JSON.stringify(validate.errors)).to.be.true;
});
失敗時に validate.errors をメッセージに含めるのが重要です。「スキーマに一致しない」とだけ出ても、どの項目が原因か分かりません。
4. 次のリクエストへ値を渡す
pm.test('IDを取得できた', () => {
const data = pm.response.json();
pm.expect(data.user_id).to.exist;
// 後続のリクエストで {{user_id}} として使える
pm.collectionVariables.set('user_id', data.user_id);
});
変数のスコープを使い分けてください。 実行中だけ持ち回る値をグローバル変数に入れると、他のコレクションを汚染します。
| スコープ | 使い分け |
|---|---|
pm.globals |
原則使わない。全体を汚染する |
pm.environment |
接続先やアカウントなど、環境ごとに変わる設定 |
pm.collectionVariables |
実行中に受け渡す一時的な値 |
pm.variables |
そのリクエスト内だけの一時変数 |
スポンサーリンク
共通処理はコレクションレベルに書く
同じスクリプトを全リクエストに貼り付けると、修正のたびに全部直すことになります。コレクションやフォルダの Pre-request / Post-response に書けば、配下すべてに適用されます。
実行順序は次の通りです。
- コレクションの Pre-request
- フォルダの Pre-request
- リクエストの Pre-request
- (リクエスト送信)
- コレクションの Post-response
- フォルダの Post-response
- リクエストの Post-response
共通の検証をコレクションレベルに置いておくと便利です。
// コレクションの Post-response
pm.test('サーバーエラーが返っていない', () => {
pm.expect(pm.response.code).to.be.below(500);
});
// 遅いリクエストを記録しておく
if (pm.response.responseTime > 2000) {
console.warn(`遅い: ${pm.info.requestName} (${pm.response.responseTime}ms)`);
}
関数をまとめたい場合は、コレクション変数に文字列として関数を入れて eval するという手法が知られていますが、可読性とデバッグ性が落ちます。共通処理はコレクションレベルのスクリプトに置く方が素直です。
CI で実行する
Postman の画面で実行しているうちは、テストの価値は限定的です。 Newman を使うと CI から回せます。
npm install -g newman
newman run collection.json \
-e environment.json \
--reporters cli,junit \
--reporter-junit-export results.xml
環境ファイルに秘密情報を入れたままコミットしないでください。 CI の秘密変数から渡します。
newman run collection.json \
-e environment.json \
--env-var "client_secret=$CLIENT_SECRET"
Jenkins に組み込む方法はSelenium・Appium・Jenkinsを使ったQAのテスト自動化で扱っています。Python から API テストを書く場合はPython活用でQA業務を効率化する5つの方法もどうぞ。
まとめ
jsonwebtokenとcryptoは Postman の組み込みに無い。pm.require('npm:crypto-js@4.2.0')などで読み込むuuid/lodash/moment/ajv/cheerioはrequire()で使える- トークンは有効期限を保存して、切れる直前だけ取り直す
pm.sendRequestではerrとres.codeの両方を確認する- 動的変数はスクリプト内では
pm.variables.replaceIn('{{$guid}}')を通す Math.random().toString(36).substring(7)は空文字になることがある- 検証では存在だけでなく型も確認する。項目が多いなら
ajvでスキーマ検証 - 受け渡しは
pm.collectionVariables。pm.globalsは使わない - 共通処理はコレクションレベルに置き、実行は Newman で CI から
秘密鍵の直書きだけは避けてください。コレクションを共有した瞬間に漏れます。 Secret 型の環境変数を使うのが最低限の対策です。