
runnを使っていて便利だと感じた機能を紹介してみる
はじめに
前回 runnについての入門しましたが、その後も利用していて便利だなと思った機能があったので一部紹介したいと思います。
筆者が利用する上で活用したユースケースを基にしているため、詳細は チュートリアル や クックブック を確認してみてください。
シナリオを共通化したい
runbookを作成していると繰り返し利用されるシナリオを共通化したいなと感じるケースがありました。
runnにはrunbookから別のrunbookを呼び出しする機能があるので、これを利用することで共通化を実現することができそうです。
前回作成した /todos エンドポイントのPost処理を別のrunbookに切り出してみます。
desc: TodosのPostテスト
runners:
api: ${API_URL}
steps:
- desc: TODOを新規作成
api:
/api/todos:
post:
body:
application/json:
title: "テストTodo"
description: "テスト用の新規作成Todo"
test: |
current.res.status == 201
&& current.res.body.title == "テストTodo"
&& current.res.body.id != ""
呼び出し側のrunbookではinludeを用いて、別のrunbookを呼び出しできます。
desc: Todos APIのCRUD操作テスト
runners:
api: ${API_URL}
steps:
- desc: TODOを新規作成
# 別のrunbookの呼び出し
include:
path: post-todos.yml
こうすることでrunbookから別のrunbookを呼び出すことができました。
別のrunbookの呼び出しをしていく際にセットで使用することが多かった機能についても紹介してみようと思います。
(以降呼び出し元を親、呼び出し先を子として表現します)
親から子へ変数を渡したい
別のシナリオの呼び出しによる共通化が叶ったところで次は親から自由にパラメータを設定したくなりました。
これはrunbookのinclude時に親のrunbookで子のrunbookの変数を上書きする機能で実現できそうです。
子のrunbookに変数を追加し、
desc: Todos TODOのPostテスト
runners:
api: ${API_URL}
# 親から引き渡される想定の変数を追加
vars:
title:
description:
steps:
- desc: TODOを新規作成
...
親のrunbookで、子の変数を上書きする部分を追加します。
desc: Todos APIのCRUD操作テスト
runners:
api: ${API_URL}
steps:
- desc: TODOを新規作成
include:
path: post-todos.yml
# 子の変数を上書き
vars:
title: "テストTodo"
description: "テスト用の新規作成Todo"
こうすることで親から子へ変数を受け渡し、親側でリクエスト値を制御することができました。
便利ですね。
親から子の値を参照したい
子でリクエストした結果の値を親で参照したいケースがあります。
/todos を例にすると、作成したTODOのidは以降のテストで使用するため親で把握したいといったようなものです。
こういったケースでは子のrunbookで公開したい値を bind することで、親から current.{別名} の形で参照することができるようです。
子側で以下のように値を bind して、
desc: TodosのPostテスト
...
steps:
- desc: TODOを新規作成
...
# 作成したTODOのidをtodoIdとして別名設定
bind:
todoId: current.res.body.id
以下のように親で参照( bind して再度別名を設定)します。
desc: Todos APIのCRUD操作テスト
runners:
api: ${API_URL}
steps:
- desc: TODOを新規作成
...
# 子のtodoIdを参照し、親で更に別名設定
bind:
todoId: current.todoId
以降は従来の bind 同様親からはtodoIdで作成したTODOのidを使用できるようになりました。
別のrunbookからの呼び出し(include)時のみ実行したい
runbookを共通化していくにあたり、親からの変数の上書きを前提としており子のrunbook単独では実行したくないと考えるようになりました。
runnではシナリオ全体をスキップするかどうかを判断する if: が用意されており、以下のように指定することで他のrunbookからの呼び出し以外はスキップすることができるようです。
if: included
上記の宣言をしたrunbookを単独で実行した場合、全てのstepがスキップされます。
例えば todos-crud.yml に上記を追加した場合は以下のようになりました。
=== Todos APIのCRUD操作テスト (./runbooks/todos-crud.yml)
--- TODOを新規作成 (0) ... skip
--- TODOの一覧を取得し作成したTODOが含まれることを確認 (1) ... skip
--- 作成したTODOを更新 (2) ... skip
--- TODOの詳細を取得し更新した内容が反映されていることを確認 (3) ... skip
--- 作成したTODOを削除 (4) ... skip
--- 削除したTODOの詳細を取得し404エラーが返ってくることを確認 (5) ... skip
また、if: は条件として変数の値などを条件にすることもできるようです。
特定の変数に値が設定されていない場合はスキップしたいなどのケースに利用できそうです。
if: vars.username != nil && vars.password != nil
スキーマの変更による影響を減らしたい
上記で共通化した post-todos.yml ですが、APIのスキーマが変更されると当然runbookに変更が必要になります。
その場合親である todos-crud.yml でリクエスト値を指定しているため、親子共に変更が必要です。
そこでリクエスト値を別ファイルで管理することでスキーマの変更への影響を削減できないかと考えました。
ありがたいことにrunnではJSONを外部ファイルとして読み込みできるので、こちらの機能を使用することで実現できそうです。
まず、リクエスト値として使用するjsonを外部ファイル化します。
{
"title": "テストTodo",
"description": "テスト用の新規作成Todo"
}
続いて子である post-todos.yml でリクエストBodyとなるJSON全体を受けるよう変数とリクエストBodyの指定を変更します
desc: Todos TODOのPostテスト
runners:
api: ${API_URL}
# JSONを受け取るように変更
vars:
requestJson:
if: included
- desc: TODOを新規作成
api:
/api/todos:
post:
body:
# 受け取ったJSONをそのままBodyに指定
application/json: "{{ vars.requestJson }}"
...
最後に親の todos-crud.yml で子の変数にjsonのファイルを引き渡します。
このとき json:// を先頭に付与しjsonファイルのパスを指定することで指定したパスのjsonをそのまま変数で扱うことができるようになります。
jsonファイルのパスは呼び出し先(子)からの相対パスなようなので、親子でディレクトリが異なる場合は注意が必要そうです。
desc: Todos APIのCRUD操作テスト
runners:
api: ${API_URL}
steps:
- desc: TODOを新規作成
include:
path: post-todos.yml
vars:
# JSONファイルを変数に指定
requestJson: json://post-todos-body.json
bind:
todoId: current.todoId
上記の形に変更することで、リクエストに使用する値を外部ファイルで管理、runbookはシナリオテストの内容に注力することができるようになりました。
また親から子へリクエストBodyとなるJSONを引き渡すことで、親側からリクエストの内容を指定可能な状態も維持されています。
JSONへ変数を埋め込む
外部ファイル化されたJSONへ変数を埋め込むことができるようなのでこちらも紹介します。
JSONへ変数を埋め込むためにGo Templateの記述方法を用いるようです。
Go Templateとして認識させるために拡張子を *.json.template に変更する必要があるようなので、拡張子を変更しつつ、titleに変数を埋め込んでみます。
{
"title": "{{ .vars.title }}",
"description": "テスト用の新規作成Todo"
}
あとは親側で vars.title を宣言しつつ適切な値を設定すればOKです。
desc: Todos APIのCRUD操作テスト
runners:
api: ${API_URL}
# JSONテンプレートに埋め込まれている変数を定義
vars:
title: variable-title
steps:
- desc: TODOを新規作成
include:
path: post-todos.yml
vars:
# テンプレートファイルを変数に指定
requestJson: json://post-todos-body.json.template
bind:
todoId: current.todoId
変数だけではなく、 bind で別名を設定した値も埋め込み可能なようなので、用途に応じて使い分けできそうです。
runbookをまとめて実行したい
CIなどに組み込む場合、複数のrunbookをまとめて実行したいケースがあります。
runnでは実行するrunbookを複数指定することで、複数のrunbookを実行することができますが、シナリオの追加・削除があるたびにコマンドを修正するのは手間ですし取りこぼしの原因にもなります。
runn run ./runbooks/auth.yml ./runbooks/todos-crud.yml --verbose --env-file=.env
シナリオの増減に影響を受けずテストを実行する方法として、パスやファイル名をワイルドカード指定して実行する方法がありました。
例えば直接実行したいシナリオを /runbooks/scenarios/ に配置していた場合、以下のように指定することでフォルダ配下のrunbookを全て実行することができます。
runn run ./runbooks/scenarios/*.yml --verbose --env-file=.env
ファイル名にもワイルドカードを指定することができ、例えばrunbooksフォルダ内でファイル名の末尾が test-scenario.yml となっているrunbookのみを実行する場合は以下のようなコマンドを実行します。
runn run ./runbooks/*-test-scenario.yml --verbose --env-file=.env
いずれを使用するかは規模やディレクトリ構造などに合わせて選択すると良さそうです。
また、より細かい指定が必要な場合はrunbookにラベルを付与する機能があるので、ラベルを付与した上で実行したいrunbookのラベルを指定することでより柔軟に制御できそうです。
まとめ
runnを使用していて便利だなぁと思う機能について(一部ですが)追加で紹介してみましたがいかがでしたでしょうか。
今回紹介させて頂いた以外にも便利な機能がたくさんあります。
気になった方は チュートリアル や クックブック を参照してもらえると更にrunnの良さを感じて頂けると思っていますので、是非確認してみてください。
runnは記述がシンプルかつ直感的で、痒いところに手が届く素晴らしいツールだと思っています。
筆者としてはE2Eに対する心理的ハードルがかなり低くなりました。
今後も様々なプロダクトで活用して品質向上に役立てたいなと思っています。







