勤怠サービスの開発チームに所属しているkarabishです。 テストに関するある課題を解決するために API テストの自動化ツールを調査しました。まだチーム内に展開していないのですが、調査結果のうちツールの選定に関する部分を備忘録として残しておこうと思います。 なぜAPIテストを自動化するのか ツールの選定方針 調査したツールたち 調査方法 調査結果 Tavern テストシナリオ テスト実行 scenarigo テストシナリオ テスト実行 runn テストシナリオ テスト実行 karate テストシナリオ テスト実行 stepci テストシナリオ テスト実行 調査しなかったツールたち まとめ なぜ API テストを自動化するのか 36協定の計算などの負荷が重たい処理はpub/sub アーキテクチャ を利用して非同期で処理していました。ただ、publish側とsubscribe側それぞれの ユニットテスト は存在していたのですが、全体に関するテストは自動化されていませんでした。そのため、pub/sub全体に関するテスト観点を API テストで自動化しようと目論んだのが発端となります。 ツールの選定方針 選定方針を挙げるとすればこの3つになるのかと思います。 CIとの親和性が高いこと dockerで扱いやすいこと yaml などのテキストに定義するだけでテストができること なぜこの3点なのかというと、 API テスト自体はCI上で実行する予定のため「CIとの親和性」が重要で、CIは Gitlab CI ( executor はdockerを利用)のためdockerで実行できる必要があり、dockerで実行するとなるとコードを書かずに yaml などで定義できればありがたい、という背景になります。 調査したツールたち 調査したツールたちは以下の5つです。調査はしませんでしたが候補としてあがったツールたちは参考までに 調査しなかったツールたち にまとめてあります。 ツール URL ライセンス 実装する言語 対応している プロトコル dockerイメージ Tavern 公式サイト , Github MIT license yaml , python http, MQTT ? scenarigo Github Apache-2.0 license yaml , golang http, gRPC ? runn Github MIT license yaml , golang http, gRPC, DB, Chrome DevTools Protocol, SSH /Local command ghcr karate 公式サイト , Github MIT license Gherkin http, GraphQL ? stepci 公式サイト , Github MPL-2.0 license yaml REST, GraphQL, gRPC, tRPC, SOAP ghcr 調査方法 OpenAPIのpetstore.yaml を利用したモック環境を用意する GET /pet/1 リク エス トに対して成功した場合(200 OKを期待値とする)を実施する % docker run --name openapi -d -p 4010:4010 stoplight/prism:3 mock -d -h 0.0.0.0 https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/petstore.yaml 9b6c556320adfacd9b3a497af35df5774b64abd406f022478254e1cc7ec42600 # cURLで実行した場合のレスポンス % curl -sS -H 'api_key: special-key' -H "Accept: application/json" http://localhost:8080/pet/1 | jq . { "name": "officia magna", "photoUrls": [ "Duis ex incididunt", "sit" ], "id": 1824362991613808600, "category": { "id": -6773680898015056000, "name": "zq8QbcVd.U6hh9W0Pl01Dpq" }, "tags": [ { "id": 623842466761203700, "name": "dolor est occaecat ea adipisicing" } ], "status": "available" } 調査結果 Tavern テストシナリオ % cat api-test.tavern.yaml test_name: sample stages: - name: GET /pet/1 request: url: http://ローカルのIPアドレス:8080/pet/1 method: GET headers: accept: application/json api_key: special-key response: status_code: 200 テスト実行 公式のdockerイメージが見つからなかったため、 chatworkさん提供のイメージ を利用させていただいた % docker run -it --rm -v ${PWD}:/tavern chatwork/tavern:1.7.0 /tavern/api-test.tavern.yaml . ------------------------------------------------------------------------------ Ran 1 tests in 0.25s OK scenarigo テストシナリオ # cat scenarios/api-test.yaml title: sample steps: - title: GET /pet/1 protocol: http request: method: GET url: 'http://ローカルのIPアドレス:8080/pet/1' header: accept: application/json api_key: special-key expect: code: 200 テスト実行 golangのdockerイメージ を実行し、scenarigoを go install でインストールする % docker run -it --rm -v ${PWD}:/tests golang:1.19.3-bullseye bash # go install github.com/zoncoen/scenarigo/cmd/scenarigo@v0.12.8 scenarigoの設定ファイルを作成する。 scenarigo config init でテンプレートは作成可能 # cat scenarigo.yaml schemaVersion: config/v1 scenarios: # Specify test scenario files and directories. # ↓このディレクトリにシナリオを配置する - scenarios pluginDirectory: ./gen # Specify the root directory of plugins. # plugins: # Specify configurations to build plugins. # plugin.so: # Map keys specify plugin output file path from the root directory of plugins. # src: ./path/to/plugin # Specify the source file, directory, or "go gettable" module path of the plugin. output: verbose: false # Enable verbose output. # colored: false # Enable colored output with ANSI color escape codes. It is enabled by default but disabled when a NO_COLOR environment variable is set (regardless of its value). # report: # json: # filename: ./report.json # Specify a filename for test report output in JSON. # junit: # filename: ./junit.xml # Specify a filename for test report output in JUnit XML format. scenarigo run でテストを実行する # cd /tests/ # scenarigo run ok scenarios/api-test.yaml 0.036s scenarigo. yaml の output.verbose を true にすることでリク エス トとレスポンスが標準出力される # scenarigo run === RUN scenarios/api-test.yaml === RUN scenarios/api-test.yaml/sample === PAUSE scenarios/api-test.yaml/sample === CONT scenarios/api-test.yaml/sample === RUN scenarios/api-test.yaml/sample/GET_/pet/1 --- PASS: scenarios/api-test.yaml (0.03s) --- PASS: scenarios/api-test.yaml/sample (0.03s) --- PASS: scenarios/api-test.yaml/sample/GET_/pet/1 (0.03s) [0] send request request: method: GET url: http://ローカルのIPアドレス:8080/pet/1 header: Accept: - application/json Api_key: - special-key User-Agent: - scenarigo/v0.12.8 response: header: Access-Control-Allow-Credentials: - "true" Access-Control-Allow-Headers: - "*" Access-Control-Allow-Origin: - "*" Access-Control-Expose-Headers: - "*" Connection: - keep-alive Content-Length: - "222" Content-Type: - application/json Date: - **** body: category: id: "1575230569806000000" name: 9O7c1pJOPktof id: "8291010298486120000" name: consequat nulla sint photoUrls: - consequ status: pending tags: - id: "1005964459763441700" name: dolor Lorem sunt elapsed time: 0.027939 sec PASS ok scenarios/api-test.yaml 0.033s runn テストシナリオ % cat api-test.yaml desc: sample runners: req: http://ローカルのIPアドレス:8080 steps: getPet: req: /pet/1: get: headers: accept: "application/json" api_key: "special-key" test: steps.getPet.res.status == 200 テスト実行 用意されているdockerイメージ を利用することで実行することができる % docker run -it --rm --name runn -v $PWD:/books ghcr.io/k1low/runn:v0.52.3-slim run /books/api-test.yaml sample ... ok 1 scenario, 0 skipped, 0 failures --debug オプションを指定することでリク エス トとレスポンスの内容が標準出力される % docker run -it --rm --name runn -v $PWD:/books ghcr.io/k1low/runn:v0.52.3-slim run --debug /books/api-test.yaml Run 'req' on 'sample'.steps.getPet -----START HTTP REQUEST----- GET /pet/1 HTTP/1.1 Host: ローカルのIPアドレス:8080 Accept: application/json Api_key: special-key -----END HTTP REQUEST----- -----START HTTP RESPONSE----- HTTP/1.1 200 OK Content-Length: 452 Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: * Access-Control-Allow-Origin: * Access-Control-Expose-Headers: * Connection: keep-alive Content-Type: application/json Date: **** {"name":"adipisicing eiusmod Excepteur nostrud","photoUrls":["ipsum dolor"],"id":4648156526148719000,"category":{"id":-1107853279573229600,"name":"8PbcHZGjikgwWWr"},"tags":[{"id":-3453614735764951000,"name":"dolore"},{"id":-6507718611499348000,"name":"ex Excepteur laboris mollit occaecat"},{"id":2671625534111551500,"name":"mollit"},{"id":-9126589410744205000,"name":"id Ut Lorem aliqua"},{"id":2174855446376759300,"name":"sed enim"}],"status":"sold"} -----END HTTP RESPONSE----- Run 'test' on 'sample'.steps.getPet sample ... ok 1 scenario, 0 skipped, 0 failures karate テストシナリオ % cat api-test.feature Feature: sample Background: * def host = 'localhost:8080' * def httpHeaders = { accept: 'application/json', api_key: 'special-key' } Scenario: GET /pet/{id} Given url 'http://' + host + '/pet/1' And configure headers = httpHeaders When method get Then status 200 テスト実行 Github からjarファイルをダウンロードしておく java -jar ${ダウンロードしたjarファイル} {テストシナリオ} で実行することができる % java -jar karate-1.3.0.jar api-test.feature 00:00:00.000 [main] INFO com.intuit.karate - Karate version: 1.3.0 00:00:00.000 [main] INFO com.intuit.karate.Suite - backed up existing 'target/karate-reports' dir to: target/karate-reports_1669956006172 00:00:00.000 [main] DEBUG com.intuit.karate - request: 1 > GET http://localhost:8080/pet/1 1 > accept: application/json 1 > api_key: special-key 1 > Host: localhost:8080 1 > Connection: Keep-Alive 1 > User-Agent: Apache-HttpClient/4.5.13 (Java/11.0.11) 1 > Accept-Encoding: gzip,deflate 00:00:00.000 [main] DEBUG com.intuit.karate - response time in milliseconds: 49 1 < 200 1 < Access-Control-Allow-Origin: * 1 < Access-Control-Allow-Headers: * 1 < Access-Control-Allow-Credentials: true 1 < Access-Control-Expose-Headers: * 1 < Content-type: application/json 1 < Content-Length: 315 1 < Date: **** 1 < Connection: keep-alive {"name":"laboris Ut","photoUrls":["elit ven","magna sint fugiat in occaecat","sit velit irure proident"],"id":6533262285796651000,"category":{"id":-5086239707500454000,"name":"jDU6rd4mMXrFOzsdIBp"},"tags":[{"id":-4193826791138492400,"name":"Duis anim"},{"id":-7590066944733139000,"name":"elit Ut"}],"status":"sold"} --------------------------------------------------------- feature: api-test.feature scenarios: 1 | passed: 1 | failed: 0 | time: 0.3456 --------------------------------------------------------- 00:00:00.000 [main] INFO com.intuit.karate.Suite - <<pass>> feature 1 of 1 (0 remaining) api-test.feature Karate version: 1.3.0 ====================================================== elapsed: 1.57 | threads: 1 | thread time: 0.35 features: 1 | skipped: 0 | efficiency: 0.22 scenarios: 1 | passed: 1 | failed: 0 ====================================================== HTML report: (paste into browser to view) | Karate version: 1.3.0 file:///${PWD}/target/karate-reports/karate-summary.html =================================================================== stepci テストシナリオ % cat api-test.yaml version: "1.1" name: sample env: host: ローカルのIPアドレス:8080 tests: getPet: steps: - name: GET /pet/1 http: url: http://${{env.host}}/pet/1 method: GET headers: accept: application/json api_key: special-key check: # http statusが200であることをチェックする status: 200 テスト実行 用意されているdockerイメージ を利用することで実行することができる Privacy に記載されている通り利用状況の収集を無効化するため STEPCI_DISABLE_ANALYTICS を指定している % docker run -it --rm -e STEPCI_DISABLE_ANALYTICS=yes -v ${PWD}:/tests ghcr.io/stepci/stepci:2.5.6 tests/api-test.yaml PASS getPet Tests: 0 failed, 1 passed, 1 total Steps: 0 failed, 0 skipped, 1 passed, 1 total Time: 0.216s, estimated 0s Workflow passed after 0.216s Give us your feedback on https://step.ci/feedback 調査しなかったツールたち 候補としては上がったが調査しなかったツールたちです。 ツール URL ライセンス cURL 公式サイト , Github ? HTTPie 公式サイト , Github BSD-3-Clause license Postman + Newman Postmanの公式サイト , NewmanのGithub NewmanはApache License 2.0 insomnia 公式サイト - api fortress 公式サイト - Assertible 公式サイト - speedscale 公式サイト - Datadog 公式サイト - Frisby 公式サイト BSD 3-Clause SuperTest Github MIT license Chakram 公式サイト , Github MIT license REST Assured Github Apache-2.0 license Pact 公式サイト , Github pact-goはMIT license Dredd 公式サイト , Github MIT license まとめ API テストの自動化ツールを調査してみました。運用していないのでどういう問題が発生するかわからないのですが、導入するのはすんなりいけそうなツールがそこそこあるなという印象です。また、冒頭に記載した通りまだチームメンバーに展開していないのでどれを採用するのかはわからないのですが、複雑なことをする予定はないので個人的にはシンプルで簡単にできそうな stepci がいいのではと思っています。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com