こんにちは、QAの@kareyama_rです。
最近お花のサブスクを始めました🌹
リモートワークで家にいる時間が長いので、部屋に季節の彩りが添えられて嬉しいです。
ソフトウェア開発において、自動試験・テスト自動化の話題はよく聞くようになりましたが、ユニットテストとE2Eテストが主体になっているように感じます。
当社では、ユニットテストとE2Eテストの間に来るインテグレーションテストとして、API試験の自動化も積極的に取り組んでいますが、E2Eテストより運用コストを抑えられるので、より対象範囲を拡大して導入していきたいと考えています。
そこで今回は、GraphQL で実装されたAPIを、テスト自動化フレームワークの Karate で検証してみました。
はじめに
最初に、知らない方のために、簡単に今回の対象について紹介します。
GitLab の GraphQL API の確認
今回は、試験対象として、GitLabが提供しているGraphQL APIを利用します。
GitLabのチケット(Issue)の操作を、GraphQL APIから実行してみます。以下の内容です。
- 一覧取得(Query): チケット一覧取得
- 1件取得(Query): チケット詳細の取得
- 更新(Mutation): チケットステータスの更新
予めテスト用にGitLabで`graphql-test`というプロジェクトを用意し、適当なIssueを作成してあります。
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というキーワードで宣言します。
実行すると、このようなレポートが出力されました。
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
実行すると、このようになりました。
最後に、「チケットステータスの更新」を行います。
更新時のレスポンスでも結果を取得できますが、更新後に再度チケット詳細をクエリして、ステータスが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
チケット情報を取得するクエリを別ファイルにしたことで、使い回しができます。
結果のレポートはこのようになりました。
まとめ
Karateを使って、GraphQLのAPIも簡単に実行・検証できました。
特にGraph QLに対してKarateでテストする際、以下の機能がサポートされているのがありがたかったです。
- QueryとMutationの実行
- Variablesの受け渡し
- .graphqlファイルの読み込み
GraphQLはリクエスト時に自由に取得フィールドを指定できる分、レスポンス形式も流動的になりますが、Karateであれば必要な箇所のみ取り出して柔軟な検証を行えます。
また、GraphQLのレスポンスは階層が深くなりがちですが、Karateならすっきりと書くことができました。
ぜひAPIテストを自動テストの1つとして取り入れてみて下さい。
それでは。
顧客のビジネスをインフラから加速するエンジニア募集! - Acroquest Technology株式会社のインフラエンジニアの求人 - Wantedlywww.wantedly.com