Taste of Tech Topics

Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

GraphQL APIをKarateで自動試験する

こんにちは、QAの@kareyama_rです。
最近お花のサブスクを始めました🌹
リモートワークで家にいる時間が長いので、部屋に季節の彩りが添えられて嬉しいです。

ソフトウェア開発において、自動試験・テスト自動化の話題はよく聞くようになりましたが、ユニットテストとE2Eテストが主体になっているように感じます。
当社では、ユニットテストとE2Eテストの間に来るインテグレーションテストとして、API試験の自動化も積極的に取り組んでいますが、E2Eテストより運用コストを抑えられるので、より対象範囲を拡大して導入していきたいと考えています。
そこで今回は、GraphQL で実装されたAPIを、テスト自動化フレームワークの Karate で検証してみました。

はじめに

最初に、知らない方のために、簡単に今回の対象について紹介します。

GraphQL とは

GraphQL はWeb APIのための仕様で、リクエスト時に利用されるクエリ言語と、クエリを実行するサーバ側のランタイムから成ります。
APIから提供されるエンドポイントは1つだけで、リクエストクエリで取得するデータフィールドを指定できます。
フロントエンドとしては、REST-APIと比べて、少ないリクエストで必要なデータが取得できるメリットがあります。感覚的には、フロントエンドから利用できるSQLのような印象です。

https://graphql.org/

Karate とは

Karate はE2Eテストや負荷テストを含むテスト自動化フレームワークであり、特にAPIテストに使いやすい機能が多く揃っています。
REST-APIのテストでよく利用されていますが、WebSocket や GraphQL のテストも可能です。
GraphQLのようにリクエスト内容によってレスポンス形式が変わるAPIでも、柔軟に試験を行うことができます。

https://intuit.github.io/karate/

GitLab の GraphQL API の確認

今回は、試験対象として、GitLabが提供しているGraphQL APIを利用します。
GitLabのチケット(Issue)の操作を、GraphQL APIから実行してみます。以下の内容です。

  • 一覧取得(Query): チケット一覧取得
  • 1件取得(Query): チケット詳細の取得
  • 更新(Mutation): チケットステータスの更新

API Docはこちら

予めテスト用にGitLabで`graphql-test`というプロジェクトを用意し、適当なIssueを作成してあります。
f:id:acro-engineer:20210913003346p:plain

GitLabが用意しているGraphiQL explorerを使い、クエリを実行してみます。

チケット一覧取得

プロジェクトを指定し、Issueの数とID・タイトルを取得します。

  query {
    project(fullPath: "sandbox17/graphql-test") {
      name
      issuesEnabled
      issues {
        count
        nodes {
          iid
          title
        }
      }
    }
  }

3件のテストデータが取得できました。

{
  "data": {
    "project": {
      "name": "graphql-test",
      "issuesEnabled": true,
      "issues": {
        "count": 3,
        "nodes": [
          {
            "iid": "3",
            "title": "issue3"
          },
          {
            "iid": "2",
            "title": "issue2"
          },
          {
            "iid": "1",
            "title": "Issue1"
          }
        ]
      }
    }
  }
}
チケット詳細の取得

プロジェクトとチケットIDを指定します。
チケットIDはvariablesとして定義し、値を自由に変えられるようにしています。

query ($iid: String!) {
  project(fullPath: "sandbox17/graphql-test") {
    issue(iid: $iid) {
      iid
      title
      state
      author {
        name
      }
      description
      labels {
        nodes {
          title
        }
      }
      dueDate
    }
  }
}
{
  "iid": "2"
}

タイトルやステータス、本文、ラベル、期日が取得できました。

{
  "data": {
    "project": {
      "issue": {
        "iid": "2",
        "title": "issue2",
        "state": "opened",
        "author": {
          "name": "kaya"
        },
        "description": "Issue2の本文です",
        "labels": {
          "nodes": [
            {
              "title": "Label02"
            }
          ]
        },
        "dueDate": "2021-09-30"
      }
    }
  }
}
チケットステータスの更新

mutationでIssueをクローズします。

mutation {
  updateIssue(input: {projectPath: "sandbox17/graphql-test", iid: "3", stateEvent: CLOSE}) {
    errors
    issue {
      iid
      state
    }
  }
}
{
    "data": {
        "updateIssue": {
            "errors": [],
            "issue": {
                "iid": "3",
                "state": "closed"
            }
        }
    }
}

Karate を利用した GraphQL のテスト

先ほどまでで実行した GitLab の GraphQL API の内容を、Karateを利用して、クエリの実行・レスポンス内容を行ってみます。
Karateプロジェクトの作成方法は、公式ドキュメントを参照してください。

まず1つ目のクエリ、「チケット一覧取得」を実行し、3件のチケットが取得できることを検証します。
Karateプロジェクトにquery_issues.featureというファイルを作成し、以下の内容を記載します。

Feature: GitLabのチケット(Issue)を取得する

Background:
  * url 'https://gitlab.com/'

Scenario: チケット一覧取得
  * text query = 
  """
  query {
    project(fullPath: "sandbox17/graphql-test") {
      name
      issuesEnabled
      issues {
        count
        nodes {
          iid
          title
        }
      }
    }
  }
  """
  Given path '/api/graphql'
  And request { query: '#(query)' } #リクエストボディを設定
  And header Accept = 'application/json' 
  When method post  #リクエストをPOST
  Then status 200  #レスポンスステータスが200であることを確認
  And match response.data.project.issues.count == 3  #チケットが3件あることを確認
  * print response

Karateはリクエストボディを定義する際、多くの場合defというキーワードを使いますが、GraphQLのクエリを渡すとJSON形式として解釈されてしまいます。
そのためGraphQLのクエリは、textというキーワードで宣言します。

実行すると、このようなレポートが出力されました。
f:id:acro-engineer:20210915222914j:plain
KarateでGraphiQL explorerと同じリクエストが実行され、レスポンスに対する検証もできました。


次に、「チケット詳細の取得」についても検証します。
今度はクエリを別ファイルに保存して読み込むようにしています。
.graphqlファイルで渡すと、defキーワードでも自動的にGraphQLのリクエストとして扱ってくれるようです(優秀!)

また、variablesを定義して、queryと一緒にリクエストボディとして渡します。

検証内容は「ステータスが"opened"であること」「ラベルに"Label02"が含まれていること」に変更します。
レスポンスデータのネストは、".."で表記を省略できます。

Scenario: チケット詳細取得
  Given def query = read('issue_detail.graphql')
  And path '/api/graphql'
  And def variables = { iid: "2" }
  And request { query: '#(query)', variables: '#(variables)' }
  And header Accept = 'application/json'
  When method post
  Then status 200
  And match response..state == ['opened']
  And match response..labels.nodes.[*] contains {title: Label02}
  * print response

実行すると、このようになりました。
f:id:acro-engineer:20210915225409j:plain

最後に、「チケットステータスの更新」を行います。
更新時のレスポンスでも結果を取得できますが、更新後に再度チケット詳細をクエリして、ステータスがCLOSEになっているか確認するシナリオを組んでみます。

Feature: GitLabのチケット(Issue)を更新する

Background:
  * url 'https://gitlab.com/'

Scenario: チケットステータス更新
  * text query = 
  """
    mutation {
        updateIssue(input: {projectPath: "sandbox17/graphql-test", iid: "3", stateEvent: CLOSE }) {
            errors
            issue {
            iid
            state
            }
        }
    }
  """
  Given path '/api/graphql'
  And request { query: '#(query)' }
  And header Accept = 'application/json'
  And header Authorization = 'Bearer <アクセストークン>'
  When method post
  Then status 200
  * print response

  # 更新結果を検証
  Given def query = read('issue_detail.graphql')
  And path '/api/graphql'
  And def variables = { iid: "3" }
  And request { query: '#(query)', variables: '#(variables)' }
  And header Accept = 'application/json'
  When method post
  Then status 200
  And match response..state == ['closed']
  * print response

チケット情報を取得するクエリを別ファイルにしたことで、使い回しができます。
結果のレポートはこのようになりました。
f:id:acro-engineer:20210916005503j:plain

まとめ

Karateを使って、GraphQLのAPIも簡単に実行・検証できました。
特にGraph QLに対してKarateでテストする際、以下の機能がサポートされているのがありがたかったです。

  • QueryとMutationの実行
  • Variablesの受け渡し
  • .graphqlファイルの読み込み

GraphQLはリクエスト時に自由に取得フィールドを指定できる分、レスポンス形式も流動的になりますが、Karateであれば必要な箇所のみ取り出して柔軟な検証を行えます。
また、GraphQLのレスポンスは階層が深くなりがちですが、Karateならすっきりと書くことができました。

ぜひAPIテストを自動テストの1つとして取り入れてみて下さい。
それでは。


顧客のビジネスをインフラから加速するエンジニア募集! - Acroquest Technology株式会社のインフラエンジニアの求人 - Wantedlywww.wantedly.com