試験効率を爆上げ、runnによるAPI試験の自動化

はじめに

 NTTビジネスソリューションズの平田です。

 API開発の現場では、多数、迅速かつ正確な試験が求められます。しかし、手作業による試験は時間がかかり、ヒューマンエラーのリスクも伴います。本記事では、API試験の自動化ツールであるrunn(らんえぬ)を活用した、API試験の効率化の一例を紹介します。runnは、YAMLを用いた直感的なテストシナリオの記述と、柔軟な自動化機能を持つオープンソースのツールです。

 本記事は2025年8月時点の情報に基づきます。

対象読者

 本記事が想定する対象読者は以下の通りです。

  • API開発に携われる方
  • CI/CDを実装される方
  • ソフトウェアの試験を実施される方
  • Amazon Cognitoを利用した開発に携われる方

背景・課題

 私が所属するバリューデザイン部システム開発部門で、あるサービスで利用するAPI群の開発・試験を実施しています。このAPI群はAmazon Cognitoから取得したトークンで認可制御を行う機能を持ち、リソース毎(API毎)に利用可能メソッドを設定できるという機能を持ちます。例えば、AというAPIに対し、ユーザXはGETのみ許可、ユーザYはGET/POST/PUT/PATCH/DELETEを許可、といった認可制御を行います。

 そのため、認証認可機能は試験の網羅性を高めて品質を担保する必要があり、多くの試験項目が必要です。具体的には、APIが呼び出された時の認可が想定通り(200 OK、403 forbiddenなど)であることを確認しますが、APIが多数あり権限パターンも複数あることから試験の数は膨大になります。

 開発当初はPythonのrequestsライブラリを利用したテストコードを作成したり、curlコマンドを使った手作業で試験を実施していました。ただ、テストコードの作成にPythonのスキルが必要だったり、試験にかかる工数が大きいなどの課題を抱えていました。そこで、API試験を効率化するために、runnの評価・導入を行いました。

ツール(runn)の簡単な紹介

 runnは、オープンソースで提供されているAPI試験ツールです。シナリオ試験に強みがあります。基本的な利用方法を開発者の方が提供してくださっています。

 さくらインターネット株式会社やフリー株式会社のような大手のサービスプロバイダでも活用されています。

 runnの特徴や基本的な使い方は他の記事や本家の記事におまかせすることとし、本記事では具体的な適用例を紹介します。

適用例① Amazon CognitoからIDtokenを取得する

 本サービスでAPI経由でにデータを登録する際の基本的なフローは以下です。

  • Amazon CognitoからIDtokenを取得する
  • APIを呼び出す際に、リクエストヘッダにIDtokenを付与する

 このフローをrunnで試験できるよう実装してみました。以下の3ファイルを準備し、コマンドラインから実行します。

runn run .\scenario.yml --verbose

 ファイルを3つに分割したのは、再利用のためです。cognito.ymlはAmazon CognitoからIDtokenを取得する関数的なYAML、call_api.ymlはAPIを呼び出すための関数的なYAML、scenario.ymlに実際のシナリオを記述します。

scenario.yml

secrets:
  # --debugオプション指定時に、以下の項目の出力を抑制する
  - PASSWORD
  - IdToken

vars:
  # 試験で利用する各種情報
  # インラインでパラメータ設定しましたが、実際には外部ファイルに切り出し、
  # または環境変数での引き渡しを推奨します
  COGNITO_URL: "https://cognito-idp.ap-northeast-1.amazonaws.com"
  COGNITO_CLIENTID: "<CognitoのClientID>"
  USERNAME: "<CognitoのユーザID>"
  PASSWORD: "<Cognitoのパスワード>"
  API_URL: "<試験対象のAPIのBaseURL>"
  API_KEY: "<試験対象のAPI呼び出しに必要なAPIキー>"

steps:
  # シナリオ
  get_Cognito_IdToken:
    desc: Amazon CognitoからIDtokenを取得する
    include:
      # 外部ファイルの呼び出し
      path: cognito.yml
    bind:
      IdToken: current.IdToken            
    # Cognitoから取得したIdTokenの参照方法:
    # - このyamlファイル内で参照:IdToken
    # - include先のYAMLから参照:parents.IdToken

  scenario1:
    desc: 試験シナリオ1
    include:
      # 外部ファイルの呼び出し
      path: call_api.yml
      vars:
        # 外部ファイル(call_api.yml)に引き渡すパラメータ(引数相当)
        # 利用するMethod
        method: "GET"
        # 試験対象のAPI Path
        ApiPath: "/v2/entities?type=testdata"
        # 期待するステータスコード
        expectedReturnStatuscode: 200

cognito.yml

desc: CognitoからIdToken取得
if: included

runners:
  req: '{{ parent.vars.COGNITO_URL }}'

steps:
  GetToken:
    desc: CognitoからIDトークンを取得する
    req:
      /:
        post:
          headers:
            # リクエストヘッダを設定
            X-Amz-Target: 'AWSCognitoIdentityProviderService.InitiateAuth'
            Content-Type: 'application/x-amz-json-1.1'
          body:
            # Cognitoの認証に必要なパラメータは、呼び出し元であるscenario.ymlの変数を
            # parent.vars.変数名 として参照しています。
            application/json: {
                  'ClientId': '{{parent.vars.COGNITO_CLIENTID}}' ,
                  'AuthFlow': 'USER_PASSWORD_AUTH' ,
                  'AuthParameters': { 
                    'USERNAME': '{{parent.vars.USERNAME}}' ,
                    'PASSWORD': '{{parent.vars.PASSWORD}}'
                    }
                  }
    test: current.res.status == 200
    bind:
      IdToken: current.res.body.AuthenticationResult.IdToken

call_api.yml

desc: APIを呼び出すYAML
if: included

runners:
  req: '{{ parent.vars.API_URL }}'

steps:
  call_api_with_Idtoken:
    req:
      '{{ vars.ApiPath }}':
        '{{ vars.method }}':
          headers:
            # リクエストヘッダを設定
            Accept: 'application/json'
            x-api-key : '{{ parent.vars.API_KEY }}'
            # Cognitoから取得したTokenを送信
            TOKEN     : '{{ parent.IdToken }}'
            User-Agent: 'runn'
    # 期待するステータスコードと比較
    test: current.res.status == vars.expectedReturnStatuscode

実行結果は以下のようになりました。

=== [No Description] (.\scenario.yml)
    --- Amazon CognitoからIDtokenを取得する (get_Cognito_IdToken) ... ok
        === CognitoからIdToken取得 (cognito.yml)
            --- CognitoからIDトークンを取得する (GetToken) ... ok
    --- 試験シナリオ1 (scenario1) ... ok
        === APIを呼び出すYAML (call_api.yml)
            --- (call_api_with_Idtoken) ... ok

1 scenario, 0 skipped, 0 failures

適用例② データの加工

 シナリオの中で、あるAPIで取得したデータを後続のAPIに引数として渡したいケースがあります。このとき、取得したデータを加工して後続のAPIに渡せると便利です。このようなニーズに対して、runnはexpr langによるデータの加工が可能です。

 例えば、あるAPIの応答に含まれるLocationヘッダの一部を切り出してSubscriprionId変数に代入したい場合、以下のように記述します。

     SubscriptionId:  'trimPrefix(current.res.headers["Location"][0],"/v2/subscriptions/")'

  具体的には、Locationヘッダが以下のような結果の場合、 /v2/subscriptions/を削除して(trimして)、 ABCDだけをSubscriptionId変数に代入できます。

 Location: /v2/subscriptions/ABCD

デメリット

 大変便利なrunnですが、実際に利用してみて気になる点もあります。(もしかしたら私たちがまだrunnを使いこなせてないだけかもしれません)

  • 条件分岐機能がない

 例えば、ある条件を満たしたらリクエストに特定のヘッダを追加する、といった条件分岐の機能が見つかりませんでした。goからの利用であれば条件分岐を利用できるようですが、YAMLのみでは難しいようです。

  • YAMLの書き方がシビア、構文エラーの原因が特定しづらい

 YAMLにインデントずれや必須項目の漏れがあるとエラーになりますが、どこで間違ったのか、何が足りないのかを特定がしにくい点があります。また、予約語(steps:、req:などの命令)なのか識別子(例:ステップの名前)がわかりづらい点もあります。慣れるまでに時間がかかるかもしれません。

まとめ

 本記事では、runnを利用したAPI試験の実装例を紹介しました。

 当初は構文エラーと原因調査に悩まされ、シンプルな試験が動くようになるまで時間を要しました。しかし、その後は出来上がったYAMLを型紙にして、コピペ+修正で試験項目をどんどん増やせます。

 最終的には約500項目の試験を実装し、シナリオ試験を約20分で一周できるようになり、試験効率が向上しました。また、リリース作業時の動作確認にも活用しており、リリース作業に伴う影響発生の早期検知にも役立っています。

 runnにはそのほかにもあらかじめ準備されたJSONテンプレート内の値を変更してPOSTしたり、CI(継続的インテグレーション)に組み込んだりと便利な機能があります。まだまだ機能を使いこなせていませんが、もっと使い込んでみたいと思います。

執筆者 

平田賀一(NTTビジネスソリューションズ(株) バリューデザイン部 システム開発部門)
PaaS/SaaSの開発・運用、社内案件のコンサル、エンジニア育成、NTT西日本Engineers' Blogの運営などに携わっています。
技術士(情報工学部門)/2025 Japan All AWS Certifications Engineers

参考資料・出典

本記事を執筆するにあたり、以下のサイトを参考にしました。

商標

  • 「AWS」「Amazon Cognito」は、Amazon Web Services,Inc.またはその関連会社の商標です。
  • 記載の会社名・製品名はそれぞれの会社の商標もしくは登録商標です。

© NTT WEST, Inc.