話題の記事

yamlでテストシナリオを書いてそのまま実行までできるAPIテストツールの新星 “runn” を試してみた

yamlでテストシナリオを書いたらそのまま実行できる……そんな夢のようなシナリオテストツール"runn"の紹介とやってみた記録です
2024.02.27

これまでのシナリオテストツールに対する課題感

シナリオテストツールといえば、 CucumberGauge といったツールが有名です。

ですが、これらのツールは「シナリオファイル」とは別に、シナリオを実行するためのコードも書かないといけません。しかも、そのコードではAPIを呼び出す処理を特定のプログラミング言語を使って書かなければなりません。その中には、HTTP Clientを実際に操作するような処理も含まれます。

私は「シナリオテストがしたい」のであって、「シナリオに沿ってAPI呼び出しを行う処理を書きたい」のではありません。こういった課題感を、ここ数年ずっと抱えてきました。

そんなとき、ついに見つけたツールが "runn" でした。

APIのシナリオテストに求めるもの

では、シナリオテストツールに求める条件とは何でしょうか?それは端的に言えば

プログラマーでなくても使える

ことではないでしょうか?

テスト設計した人がそのままシナリオを書いてテストを実行する。これができたら最高だと思いませんか?

要素に分解

具体的には、以下のような条件があるとうれしいなと思います。

  • シナリオ、テストケースの作りやすさ
    • シナリオと同期して具体のテストケースもすぐに直せる
  • シナリオ、テストケースの読みやすさ
    • シナリオがすっと読み下せる
    • その上で、具体で「何を」やっているかもすぐに分かる
      • 「どのように」でないのがポイント
      • どんな条件で何を期待しているかがわかる *動かしやすさ
    • シナリオを書く人が手元でさっと動かせる
      • 複雑な環境構築はしたくない

runnなら全部解決できる

そこで、今回取り上げる"runn"です。"runbook"という形式でシナリオを書けば、そのまま実行できるシナリオファイルになります。

このツールであれば、私が求める条件をきれいにクリアできます。

  • シナリオ、テストケースの作りやすさ
    • シナリオと同期して具体のテストケースもすぐに直せる
      • どこに対して何をどうするか書けば良い
        • HTTPメソッド
        • URL
        • パラメータ
  • シナリオ、テストケースの読みやすさ
    • シナリオの説明とともに、実際のテストケースがそのまま確認できる
      • 複雑な条件なら外部化も可能
  • 動かしやすさ
    • ワンバイナリを置くだけ
      • 各種パッケージ管理ツールでの導入もできる
    • yamlでシナリオを書いたら後はコマンドを動かすだけ

やってみよう

OAuthで認可を行うAPIに対して、シナリオを作ってテストしてみました。

シナリオでやりたいこと

  1. OAuth Token Endpoint を実行してアクセストークンを発行
  2. 発行されたアクセストークンを使ってリソースを登録する

やってみた

テストシナリオは以下のとおりです。コメントで補足しているところといっしょに読んでみてください。

scenario.yaml

desc: アクセストークンを発行し、テナントを登録する
runners:
  req: http://localhost:8080
steps:
  createToken:
    desc: アクセストークンを発行する
    req:
      /oauth/token:
        post:
          body:
            application/json:
              # リクエストパラメータをKey-Value形式で指定する
              subject: "example"
              issuer: "http://localhost"
              expires_in: 86400
    test: |
      # 処理が成功し、JWT形式のアクセストークンが発行されたか検証する
      current.res.status == 200
      && (current.res.body.access_token startsWith "eyJ") == true
    # 以後のリクエストで使えるよう、発行されたトークンを束縛しておく
    bind:
      access_token: current.res.body.access_token

  bindTenantCode:
    bind:
      # UUIDを生成してテナントコードとして使用する
      tenant_code: faker.UUID()

  createTenant:
    desc: テナントを登録する
    req:
      /tenants:
        post:
          headers:
            # 発行されたアクセストークンを参照して指定する
            authorization: "Bearer {{ access_token }}"
          body:
            application/json:
              # bindしたテナントコードを利用する
              tenant_code: "{{ tenant_code }}"
              name: "テスト用テナント"
              title: "テスト用テナントタイトル"
    test: |
      # 処理が成功し、指定したのと同じテナントコードでリソースが作成されたか検証する
      current.res.status == 200
      && current.res.body.tenant_code == tenant_code

実行結果は以下のとおりです。--verbose でなく --debug オプションを指定すると、実際にAPI呼び出しのリクエスト/レスポンスの内容も確認できます。

$ runn --version
runn version 0.99.3

$ runn run scenario.yaml --verbose
=== アクセストークンを発行し、テナントを登録する (scenario.yaml)
    --- アクセストークンを発行する (createToken) ... ok
    --- (bindTenantCode) ... ok
    --- テナントを登録する (createTenant) ... ok


1 scenario, 0 skipped, 0 failures

世界が変わる

これまでは、シナリオテストといえば「わたしシナリオつくる人、あなたコード書く人」のように役割を分けざるを得ないことが多くありました。しかも、シナリオに対応するテストも、テスターが Postman のようなツールを使って手動で実行するか、シナリオに沿ってAPIを呼び出すテストプログラムをプログラマーが別途書く必要がありました。

しかし、runnのようなツールがあれば、低コストに「動作する受け入れテスト」から始めることができます。ユースケースを元にしたシナリオを作ってシナリオレベルでレビューもできますし、そのシナリオを実行してパスするようにプログラムを書けば、事前に「合意」した仕様を担保できるため、リファクタリングや仕様の変更といった開発サイクルをすばやく回すこともできます。

今後、実際にプロダクト開発で使ってみて、開発プロセス含めて活用方法を考えていきたいと思います。

余談

実は「ツールのコミッターと手軽にコミュニケーションが取れる」ということも、runnの好きなところです。

実際に runnチュートリアル を動かしながら試していたところ、途中でうまく動かない場面に出くわしました。ですが、チュートリアルの著者である @katzchum に雑に X(旧twitter) でメンションしたところ、素早く確認、修正してもらえました。

また、runnの作者の @k1LoW にも、ロゴをアイキャッチで使用することについて雑メンションしたところ、快く許諾いただきました。

今後もぜひ積極的にフィードバックし、何らかの形で貢献し続けられたらと思っています。

参考