第2章:シナリオ記述編¶
「YAMLでテストを書く?そんなことできるの?」
「複雑なAPIの連携テストをシンプルに表現したい」
「チーム全員が読み書きできるテストシナリオが欲しい」
できます! runnのシナリオ記述を学べば、プログラミング知識がなくても複雑なE2Eテストが書けるようになります。
Runbook - あなたのテストの台本¶
runnでは、テストシナリオをRunbookと呼びます。
まるで映画の台本のように、何をどの順番で実行するかを記述する、それがRunbookです。
なぜRunbookなのか?¶
従来のテストコード:
// 読むのも書くのも大変...
const response = await fetch('/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice' })
});
assert(response.status === 201);
runnのRunbook:
desc: シナリオの説明 # このシナリオが何をテストするか
labels: # シナリオの分類ラベル(フィルタリングに利用)
- api
- user
runners: # 使用するランナーの定義
blog: http://localhost:8080
vars: # シナリオで使用する変数
baseURL: http://localhost:8080
timeout: 30
steps: # 実行するステップの定義
- blog:
/users:
get: {}
test: current.res.status == 200
見てください! これだけ直感的に書けるんです。
Runbookの5つの要素をマスターしよう¶
Runbookは5つの主要セクションで構成されています。それぞれが重要な役割を持っています:
1. desc - シナリオの目的を宣言¶
何をテストするのか、一目で分かるように記述します。チームメンバーへの最高のドキュメントです。
2. labels - スマートな分類¶
--label
オプションで特定のテストだけを実行。大規模プロジェクトの必須テクニック!
3. runners - 接続先の定義¶
HTTP、gRPC、データベース...すべての接続先をここで定義。マルチプロトコル対応の心臓部!
4. vars - 変数で効率化¶
繰り返し使う値を変数化。DRY原則をYAMLでも実現!
5. steps - 実行ステップの記述¶
テストの本体。順番に実行されるアクションのリストです。
2つの記述スタイル - あなたはどっち派?¶
runnは2つの記述スタイルを提供しています。プロジェクトに合わせて選べる柔軟性!
スタイル1: リスト形式 - シンプル&ストレート¶
インデックスでステップを参照する、最もシンプルな方法:
desc: ユーザー作成とログインのテスト(リスト形式)
runners:
blog: http://localhost:8080
steps:
- blog: # steps[0]
/users:
post:
body:
application/json:
name: "Alice"
email: "alice@example.com"
test: steps[0].res.status == 201
- blog: # steps[1]
/login:
post:
body:
application/json:
email: "{{ steps[0].res.body.email }}"
password: "password123"
test: |
steps[1].res.status == 200 &&
steps[1].res.body.token != null
メリット:
- 学習コストゼロ
- 短いシナリオに最適
- steps[0]
、steps[1]
...と直感的
スタイル2: マップ形式 - 可読性の極み¶
名前付きステップで、まるで関数のように扱える:
desc: ユーザー作成とログインのテスト(マップ形式)
runners:
blog: http://localhost:8080
steps:
create_user: # 名前付きステップ
blog:
/users:
post:
body:
application/json:
name: "Alice"
email: "alice@example.com"
test: steps.create_user.res.status == 201
login_user: # 名前付きステップ
blog:
/login:
post:
body:
application/json:
email: "{{ steps.create_user.res.body.email }}"
password: "password123"
test: |
steps.login_user.res.status == 200 &&
steps.login_user.res.body.token != null
メリット:
- ステップの意図が明確
- 長いシナリオでも迷わない
- steps.create_user
のように意味のある名前で参照
プロのアドバイス: 10ステップ以上のシナリオなら、迷わずマップ形式を選ぼう!
変数マスターへの道¶
変数定義の極意¶
runnの変数システムは驚くほどパワフルです:
vars:
# 静的な値
apiVersion: v1
timeout: 30
# 環境変数から取得
apiKey: ${API_KEY:-test-api-key}
environment: ${ENV:-development} # デフォルト値付き
# 複雑なデータ構造
testUser:
name: "Test User"
email: "test@localhost"
roles:
- admin
- user
runners:
httpbin: http://localhost:8080
steps:
test_vars:
httpbin:
/anything:
get:
headers:
X-API-Key: "{{ vars.apiKey }}"
test: current.res.status == 200
show_vars:
desc: "変数の値を表示"
# 変数の値をデバッグ出力
dump: vars.apiKey
注目ポイント:
- 環境変数から自動取得(${API_KEY}
)
- デフォルト値の設定(${ENV:-development}
)
- 複雑なデータ構造もOK
変数参照の魔法¶
定義した変数を自在に活用:
runners:
httpbin: http://localhost:8080
vars:
apiVersion: "v1"
apiKey: "test-api-key"
timeout: "30"
steps:
- httpbin:
/get?v={{ vars.apiVersion }}: # パス内での変数展開
get:
headers:
X-API-Key: "{{ vars.apiKey }}"
X-Timeout: "{{ vars.timeout }}"
test: |
current.res.status == 200
ワンポイント: {{ vars.変数名 }}
の記法で、どこでも変数を展開できます!
実践!ステップ記述の完全ガイド¶
HTTPリクエストの全機能¶
これがrunnの真骨頂! すべての機能を詰め込んだ例:
runners:
blog: http://localhost:8080
vars:
environment: "test"
userId: "123"
token: "test-token"
steps:
# まずユーザーを作成
create_user:
blog:
/users:
post:
body:
application/json:
name: "Test User"
email: "test@localhost"
test: current.res.status == 201
api_call:
desc: ユーザー情報を更新する # ステップの説明
if: vars.environment == "test" # 条件付き実行
blog:
/users/{{ steps.create_user.res.body.id }}:
put:
headers:
Content-Type: application/json
Authorization: "Bearer {{ vars.token }}"
body:
application/json:
name: "Updated Name"
email: "updated@localhost"
timeout: 10s # タイムアウト設定
test: | # テストアサーション
current.res.status == 200 &&
current.res.body.name == "Updated Name"
dump: current.res # デバッグ用の値出力
標準出力には以下のように出る!
{
"body": {
"email": "updated@localhost",
"id": 1,
"name": "Updated Name"
},
"cookies": {},
"headers": {
"Content-Length": [
"59"
],
"Content-Type": [
"application/json"
],
"Date": [
"Tue, 22 Jul 2025 01:12:08 GMT"
]
},
"rawBody": "{\"id\":1,\"name\":\"Updated Name\",\"email\":\"updated@localhost\"}\n",
"status": 200
}
驚きの機能群:
- 条件付き実行(if
)
- タイムアウト設定
- 複雑なテストアサーション
- デバッグ用のダンプ機能
データベースも同じ感覚で¶
SQLクエリもYAMLで自然に記述:
# TODO: Run this example in a test database
runners:
testdb: sqlite:///tmp/runn-test.db
vars:
testEmail: "alice@example.com"
steps:
setup-db:
db:
testdb:
query: |
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
insert-user:
db:
testdb:
query: |
INSERT INTO users (name, email)
VALUES ('Alice', '{{ vars.testEmail }}')
check-user:
db:
testdb:
query: |
SELECT id, name, email, created_at
FROM users
WHERE email = $1
params:
- "{{ vars.testEmail }}"
test: |
len(steps.check_user.rows) == 1 &&
steps.check_user.rows[0].name == "Alice"
ポイント: HTTPもDBも、同じtest
構文でアサーション!統一感が素晴らしい。
現場で使える!実践シナリオ集¶
マスターピース1: 完璧なCRUD操作¶
実際のプロジェクトで使えるレベルのシナリオ:
desc: ブログ記事のCRUD操作をテスト
runners:
blog: http://localhost:8080/api
vars:
authorId: "author-123"
steps:
# 1. 記事を作成
create_post:
blog:
/posts:
post:
body:
application/json:
title: "テスト記事"
content: "これはテスト記事です"
authorId: "{{ vars.authorId }}"
test: |
steps.create_post.res.status == 201 &&
steps.create_post.res.body.id != null
# 2. 作成した記事を取得
get_post:
blog:
/posts/{{ steps.create_post.res.body.id }}:
get: {}
test: |
steps.get_post.res.status == 200 &&
steps.get_post.res.body.title == "テスト記事"
# 3. 記事を更新
update_post:
blog:
/posts/{{ steps.create_post.res.body.id }}:
put:
body:
application/json:
title: "更新されたテスト記事"
content: "内容も更新しました"
test: steps.update_post.res.status == 200
# 4. 更新を確認
verify_update:
blog:
/posts/{{ steps.create_post.res.body.id }}:
get: {}
test: |
steps.verify_update.res.body.title == "更新されたテスト記事"
# 5. 記事を削除
delete_post:
blog:
/posts/{{ steps.create_post.res.body.id }}:
delete: {}
test: steps.delete_post.res.status == 204
# 6. 削除を確認
verify_delete:
blog:
/posts/{{ steps.create_post.res.body.id }}:
get: {}
test: steps.verify_delete.res.status == 404
1 scenario, 0 skipped, 0 failures
学べること: - Create → Read → Update → Delete の完全なフロー - ステップ間でのID受け渡し - 削除後の404確認まで網羅
YAML記述の秘伝テクニック¶
テクニック1: 複数行文字列¶
長いテキストも美しく記述:
runners:
httpbin: http://localhost:8080
steps:
- desc: |
これは複数行の
説明文です
httpbin:
/post:
post:
body:
text/plain: |
複数行の
テキストデータ
テクニック2: アンカー&エイリアス¶
DRYの極みを実現:
# 共通のヘッダーを定義
commonHeaders: &headers
Content-Type: application/json
X-API-Version: "1.0"
runners:
blog: http://localhost:8080
steps:
- blog:
/users:
get:
headers:
<<: *headers # 共通ヘッダーを使用
Authorization: "Bearer token123"
効果: 共通設定を一箇所で管理!メンテナンスが劇的に楽に。
テクニック3: 環境別設定の管理¶
開発・本番環境を賢く切り替え:
vars:
baseURL: ${BASE_URL:-http://localhost:8080}
apiKey: ${API_KEY:-test-api-key}
environment: ${ENV:-development}
# 環境別の設定をマップで管理
config:
development:
timeout: 60
retries: 3
production:
timeout: 30
retries: 1
runners:
blog: "{{ vars.baseURL }}"
steps:
test_request:
blog:
/test:
get:
headers:
X-API-Key: "{{ vars.apiKey }}"
test: current.res.status == 200
あなたは今、YAMLマスター!¶
おめでとうございます!
この章を読み終えたあなたは、もう立派なRunbook作成者です。
習得したスキル¶
✅ Runbookの5つの要素を完全理解
✅ リスト形式とマップ形式を使い分けられる
✅ 変数を活用してDRYなシナリオが書ける
✅ 実践的なシナリオが作成できる
✅ YAMLの高度なテクニックを身につけた
次なる高みへ¶
でも、これはまだ序章です!
次章では、runnの最強の武器である式評価エンジンを学びます。前のステップの結果を自在に操り、条件分岐やフィルタリングを駆使する...
もっとパワフルなテストが書けるようになります!
ヒント: この章で学んだ技術を組み合わせれば、どんな複雑なシナリオも表現できます。まずは小さなシナリオから始めて、徐々に大きくしていきましょう!