はじめに
こんにちは。マイグレーションチームの藤本です。
ZOZOTOWNはオンプレミスとクラウドのハイブリッドで動いており、その内、オンプレミス側のアプリケーションはClassic ASPとストアドプロシージャの組み合わせで実装されています。
私たちのチームでは、そのClassic ASPとストアドプロシージャの廃止を目標に、まずは参照系の処理をWeb APIで置き換える作業をしています。この記事では、Karate
を使って参照系の処理を置き換えるWeb API(以後、参照系API
)のE2Eテストを実現するための取り組みについてご紹介します。
全体に影響する修正とテストの必要性
フレームワークのバージョンアップ
冒頭の通り、参照系APIは商品やショップの情報を取得すると言ったZOZOTOWNでも最も古い部類に入る機能たちを、まずはAs−IsでそのままWeb API化することを目的とするものです。そのため、いわゆる「REST API」とは異なり、1つ1つのWeb APIが様々な機能を提供しています。同じエントリポイントであっても、その時のパラメーターによって挙動が全く異なるため、網羅的な動作確認が重要です。
この参照系APIは少し古いバージョンのSpring Bootで実装されており、ある時バージョンアップが必要となりました。
バージョンアップ時のテスト
Spring Bootは各バージョンごとにマイグレーションガイドを作成してくれているので、手順通りに進めればそれほど躓くことなくバージョンアップができます。しかしながら、影響調査の過程で対象から漏れたり勘違いしたり、あるいはまだ修正されていないバグにぶつかってしまう可能性は十分にあります。Spring Bootのようなフレームワークのバージョンアップの場合には、できるだけ広い範囲をテストするべきです。
参照系Web APIで準備しているテストと課題
参照系APIは作成されたときからユニットテストが作られており、現在も機能の改修を続けながらControllerやService単位でテストもあわせて修正されています。
一方で、当時E2Eテストは不十分でした。主な役割の1つである商品やショップを探す機能は、エンドポイントやパラメーターの組み合わせのパターンが多く、この網羅が難しい状況でした。
このような状況でも、ある程度の網羅性を確保しつつE2Eテストを実施するために、Karateをテストツールとして採用しました。
Karateの準備
Karateを選択した理由
Karateはテスト自動化ツールのひとつで、MockサーバーやUIテストの機能も備えています。 github.com
Karateを選択した大きな理由はJavaで作られていることです。参照系APIの開発はJavaで行っており、すでにチームメンバーのPCにも実行環境が整っています。 Karateの実行方法はいくつか用意されていますが、JARファイルを使ったスタンドアローンな実行方法を選択することで、簡単に実行できます。
- JARファイルを取得する
- javaコマンドで実行する
今回はこの方法を採用しました。
テストを実行するための準備
本格的なパターンを作成する前に、まずは簡単なテストが実行できるのか試しました。
Spring Boot Actuatorを導入済みだったので、ヘルスチェックのエンドポイントにアクセスするテストシナリオを作成しました。ファイル名はtest.featureです。
Feature: Test Background: * def baseUrl = 'http://localhost:8080' Scenario: test Given url baseUrl + '/actuator/health' When method get Then status 200
/actuator/healthにGETでアクセスして、HTTPスタータスが200であればテストOKという簡単な内容です。 実行するときはKarateのJARファイルと先程のテストシナリオのファイルを指定します。
java -jar karate.jar test.feature
成功すると次のように結果が表示されます。
06:36:22.721 [main] DEBUG com.intuit.karate - request: 1 > GET http://localhost:8080/actuator/health 1 > Host: localhost:8080 1 > Connection: Keep-Alive 1 > User-Agent: Apache-HttpClient/4.5.13 (Java/11.0.12) 1 > Accept-Encoding: gzip,deflate 06:36:22.875 [main] DEBUG com.intuit.karate - response time in milliseconds: 149 1 < 200 1 < Content-Type: application/vnd.spring-boot.actuator.v3+json 1 < Transfer-Encoding: chunked 1 < Date: XXX, XX Sep 2021 06:36:22 GMT 1 < Keep-Alive: timeout=60 1 < Connection: keep-alive {"status":"UP","groups":["liveness","readiness"]} --------------------------------------------------------- feature: test.feature scenarios: 1 | passed: 1 | failed: 0 | time: 0.6267 --------------------------------------------------------- 06:36:23.834 [main] INFO com.intuit.karate.Suite - <<pass>> feature 1 of 1 (0 remaining) features/local/test.feature Karate version: 1.1.0 ====================================================== elapsed: 2.80 | threads: 1 | thread time: 0.63 features: 1 | skipped: 0 | efficiency: 0.22 scenarios: 1 | passed: 1 | failed: 0 ====================================================== HTML report: (paste into browser to view) | Karate version: 1.1.0 file:///target/karate-reports/karate-summary.html ===================================================================
curlコマンドにverboseオプションをつけて実行したときと似た内容に加えて、実行にかかった時間とテスト結果のサマリが出力されます。出力の最終行にもある通り、テスト結果のサマリはレポートファイルとしても出力されます。これでKarateの準備はOKです。
テストパターンの準備
Karateの準備ができたら次はテストパターンの準備です。テストシナリオをできるだけ本番に近づけるため、Splunkのログを利用することにしました。ログからリクエスト内容を取得することで、実際の使われ方に近いテストを作成します。なお、ZOZOTOWNでのSplunk利用については、以下の記事をご覧ください。
Splunkでテストパターン作成
抽出したサーチ文の例がこちらです。
uri_path="/api/*" http_method="GET" | strcat uri_path "?" uri_query uri | strcat "Given url baseUrl + '" uri "'|" given | strcat given + "When method get|" when | strcat when + "Then status 200||" then | table then | rex field=then mode=sed "s/\|/\n/g"
URLから取得できるパスとクエリ、KarateのDSLを文字列として結合して、最後に改行を置換しています。このサーチ文で検索した結果をCSVとしてダウンロードするだけで、ほぼテストシナリオは完成です。具体的には以下のような結果が得られます。
Given url baseUrl + '/actuator/health' When method get Then status 200
この結果に、リクエスト先などの先頭行を加えると、前述のtest.featureと同じものが完成です。
Feature: Test Background: * def baseUrl = 'http://localhost:8080' Scenario: test Given url baseUrl + '/actuator/health' When method get Then status 200
Karateは非常にシンプルなDSLを持っており、このようにアクセスログからそのままテストシナリオを作ることが可能です。参照系APIのようにパラメーターの組み合わせや動作が複雑なWeb APIを扱う場合、とても便利な特徴です。
なお、この例で作成しているテストシナリオは、HTTPステータスが200であることしか確認していません。状況に応じてレスポンス内容のAssertionを追加ください。
なお、今回Karateを導入するにあたって、サーチ文による検索を1か月という期間で絞りました。そのため、数ヶ月に1回のアクセスやレアなパラメーターの組み合わせは含まれていない可能性があります。想定されるパターンを隈なく探し出すことにコストを掛けるよりも、まずは仕組みを作って実行できる環境を整えることを優先しました。これは参照系APIに、マスタデータを更新したり決済したりといった、クリティカルな機能が含まれていないからこその判断です。
クリティカルな機能が含まれるWeb APIの場合は、純粋に全パターンを網羅するテストを実施するほうが望ましいでしょう。
作成したテストパターンを使う
作成したテストパターンでテストを実行します。実行自体は前述と同様で、テストシナリオとして指定するファイルを変更するだけです。参照系APIは非公開APIのため、URLはダミーです。
Feature: Test api Background: * def baseUrl = 'http://localhost:8080/api' Scenario: Pass Through Test Given url baseUrl + '/v1/xxxxx/yyyyy' When method get Then status 200 Given url baseUrl + '/v1/xxxxx/yyyyy/?id=hoge' When method get Then status 200 ...省略...
java -jar karate.jar PassThroughTest.feature
...省略... 04:00:43.703 [main] DEBUG com.intuit.karate - response time in milliseconds: 53 67 < 200 67 < Content-Type: application/json 67 < Transfer-Encoding: chunked 67 < XXX, XX Sep 2021 04:00:43 GMT 67 < Keep-Alive: timeout=60 67 < Connection: keep-alive {"result":[]} --------------------------------------------------------- feature: features/local/PassThroughTest.feature scenarios: 1 | passed: 1 | failed: 0 | time: 2.4704 --------------------------------------------------------- 04:00:46.181 [main] INFO com.intuit.karate.Suite - <<pass>> feature 1 of 1 (0 remaining) features/local/PassThroughTest.feature Karate version: 1.1.0 ====================================================== elapsed: 6.30 | threads: 1 | thread time: 2.47 features: 1 | skipped: 0 | efficiency: 0.39 scenarios: 1 | passed: 1 | failed: 0 ====================================================== HTML report: (paste into browser to view) | Karate version: 1.1.0 file:///target/karate-reports/karate-summary.html ===================================================================
現時点で67件のテストを実行して、およそ7秒前後の実行時間になっています。これからパターンを増やしていくと実行時間も伸びていきますが、テストが苦になるほどの時間はかからない見込みです。
まとめ
Karateを使ったWeb APIのテストを実現するための取り組みについてご紹介しました。ライブラリのバージョンアップといった全体に影響する変更も、テストを実行できる環境があることで、これまでよりも安心して行えるようになりました。ただ、今回作成したテストシナリオもまだまだ万全ではなく、今後はテストパターンや確認する内容を増やしていく必要があります。引き続き安定してサービスを提供できるよう、ZOZOTOWNの改善を進めていきます。
さいごに
ZOZOTOWNのリプレイスはこれからも続きます。機能の追加とパフォーマンスの維持、安定稼働を両立しながら進めなければなりません。
ZOZOテクノロジーズでは、一緒にサービスを成長させていく仲間を募集中です。ご興味のある方は以下のリンクからぜひご応募ください。