runnによるAPIシナリオテスト自動化を試してみた


こんにちは! VPoEの青木啓剛です。

現在、QA領域のマネジメントを兼務しておりまして、半年ほど前に コンパウンドスタートアップにおける理想のQAについて考えた という記事を執筆したものです。このときに思い描いた理想のQAへ少しずつ近づくために色々なトライをしているのですが、そのひとつとしてAPIシナリオテストツール「runn」を試してみた中で感じた利点などについて紹介したいと思います。

runnとは?

runn(ランエヌ)はオープンソースのシナリオテストツールです。YAMLのフォーマットで宣言的にテストシナリオを記述することができ、定型的なテストの実行に大変便利です。APIのシナリオテストを実行するのに便利な機能もいろいろと組み込まれており、そういった周辺機能も含めてコードでテストを定義することで再利用性の高いテスト整備が可能となります。

github.com

技術検証の背景

検証をはじめた発端は、Integration Testの充実をプロダクト横断で進めることを加速できるのでは、との期待からでした。estieの各プロダクトはバックエンド領域で異なる開発言語(Rust, Node.js, Ruby, Pythonなど)を使って開発されており、統一された記法でIntegration Testを整備できると良いのではという仮説です。

そうした未来をつくる選択肢になるかどうか、現実の技術構成を踏まえていくつかの観点から検証をすることにした次第です。

GraphQLのテスト

estieでは、プロダクトにおけるフロントエンド―バックエンド間のAPIだけでなく、プロダクト同士の連携やプラットフォームとしての共通機能提供を目的とした社内向けAPIがいくつかあります。これらの多くはGraphQLによって提供されています。

一見するとrunnのドキュメントには以下の記述があり、GraphQLにはネイティブ対応していないように読めてしまいそうになるのですが、GraphQLでのテストについても問題なく実行可能です。

Support HTTP request, gRPC request, DB query, Chrome DevTools Protocol, and SSH/Local command execution

具体的には、以下のようにテスト定義を記述することで問題なくリクエストを検証することができました。GraphQLの仕様を考えれば当たり前の話ではありますね。(ちなみにGitHubにもそのように使ってねという趣旨の作者のコメントが残っていました。)

desc: GraphQLエンドポイントのテストのサンプル
runners:
  req: http://localhost:8080/api
steps:
  queryBuildings:
    req:
      /graphql:
        post:
          body:
            application/json:
              query: |
                query Buildings {
                  buildings {
                    id
                    name
                  }
                }
    test: current.res.status == 200
  queryBuilding:
    req:
      /graphql:
        post:
          body:
            application/json:
              query: |
                query Building($id: ID) {
                  building(id: $id) {
                    id
                    name
                    address {
                      prefecture
                      city
                    }
                  }
                }
              variables:
                id: "{{ steps.queryBuildings.res.body.data.buildings[0].id }}"
    test: current.res.status == 200

認証基盤であるAuth0と組み合わせたテスト

昨年ごろよりestieでは認証・認可の基盤を統合する取り組みを進めています。ID基盤にはAuth0を採用し、各プロダクトのAPIはAuth0によって認可されたリクエストのみが叩けるように制限をしています。

実際にCI環境などでテストを実行する際にはアクセストークンを取得するための仕組みが必要ですが、このようなときにもrunnでは認証・認可のフローを同じディレクトリに auth.yml というファイルで作成するなど別ファイルに切り出すことで可読性高くテストを実行することができます。

desc: 認可のサンプル
runners:
  req: https://example-tenant-name.jp.auth0.com
vars:
  clientId: ${AUTH0_CLIENT_ID}
  clientSecret: ${AUTH0_CLIENT_SECRET}
  username: ${USERNAME}
  password: ${PASSWORD}
steps:
  auth:
    req:
      /oauth/token:
        post:
          body:
            application/x-www-form-urlencoded:
              grant_type: password
              client_id: '{{ vars.clientId }}'
              client_secret: '{{ vars.clientSecret }}'
              username: '{{ vars.username }}'
              password: '{{ vars.password }}'
              scope: openid profile email
    test: current.res.body.token_type == 'Bearer'
    bind:
      accessToken: current.res.body.access_token

このようにテストを定義することで、OAuth 2.0で定義されたResource Owner Password Credentials Grantの認可プロセスでアクセストークンを取得することができます。なお、通常のウェブアプリケーションではResource Owner Password Credentials Grantを使うことはないため、Auth0のApplication設定を一部変更し、開発・検証環境においてこのフローを有効にする必要がありました。

あとは上記のサンプルで取得したアクセストークンを使って以下のようにリクエストを実施するとアプリケーションの認証機構を突破することができます。

desc: 認可を呼び出すテストのサンプル
runners:
  req: http://localhost:8080/api
steps:
  auth:
    include: auth.yml
  query:
    req:
      /graphql:
        post:
          headers:
            Authorization: Bearer {{ steps.auth.accessToken }}
          body:
            application/json:
              ...(以下略)

他の宣言的なAPIテストツールの比較

runn以外にも、宣言的にAPIテストを記述し実行できるツールが複数あります。runnを試すにあたっては、これらのツールについても調査し、どれが自分たちにとって最適かを比較しました。

runnStep CITavernKarate
GitHub Stars0.4K1.6K1K7.9K
開発言語GoTypeScriptPythonJava
テスト記述言語YAMLYAML/JSONYAML独自言語
GraphQL対応
ファイル分割非対応不明
その他所感実行ステップにSQLのクエリを直接記述できるのがseedデータの準備や更新系のみ存在するAPIのテストの観点で強力ドキュメントも充実しており必要十分機能面で他のツールよりは劣る印象この手のツールの老舗だが独自言語のクセが強いのは好みがわかれそう

runnは他のツールと比較してGitHub Starsが少ない新興ツールですが、機能やドキュメントの充実さ、実行速度の面で魅力的だと感じました。大量のテストケースをCIで実行する際には実行速度がネックとなることも多いですが、実際にrunnとStep CIを有力候補として実行速度を比較したところ、シンプルなシナリオで実行時間に30秒程度の差が出ました。StepCIはDockerコンテナとして動くため、コンテナ起動から最初のテスト実行までの待ち時間がどうしても一定必要で、Go製バイナリを直接配布しているrunnのほうが速度面で優位なのでしょう。

最後に

今回、いくつかの観点からの検証を通して、複数プロダクトでのIntegration Testの充実においてrunnが有力なツールになり得ることを確認できたと考えています。今後プロダクト開発においてどのような位置づけとするかは現在絶賛検討中ですが、何らかの形で品質向上のために活用していきたいと考えています。

最後になりますが、私たちestieではこのようなトライアンドエラーを通して品質の良いプロダクト提供のための仕組みづくりを一緒に担っていただける仲間を絶賛募集中です。ご興味をお持ちいただいた方はぜひカジュアル面談にご応募ください!

hrmos.co

hrmos.co

© 2019- estie, inc.