みなさん、E2Eテストを実践されているでしょうか? Seleniumをはじめとして、Webフロントや、ネイティブアプリのためのテストスイートの進化は、昨今目覚ましいものがあり、すでに取り入れているプロジェクトも多いことでしょう。 また、PuppeteerなどのヘッドレスなWebテスト自動化ツールの出現により、CIサーバーでE2Eテストを実施することも難しくなくなってきています。 一方で、頻繁にUI改善が行われたり、ABテストが行われるのがフロントエンドの宿命ですから、せっかく作成したE2Eテストが動かなくなったり、その改修の工数が頻繁に発生することに悩まされることも多いのではないでしょうか。 リリース直前に作成したE2Eテストが失敗すると、仕様変更なのかデグレイドなのかを確認しなければなりません。 必要ならばリリースを中断してテストを修正するというのは、あまりやりたくないものです。 もしテストの失敗を許容してリリースする判断がなされるなら、そのE2Eテストは近い将来廃止される悲しい未来が訪れる可能性が高いです。
ところで、PC-WebとSP-Web、ネイティブアプリの機能差をなくし、バックエンドはフロントエンドに依存しないRESTfulなAPIを提供するというのは、最近多く見られる傾向です。 フロントエンドは、デバイスを問わず同じ機能を提供するために同じAPIを呼び出します。 このケースだと、E2Eテストで自動化される画面操作は、それに伴って発せられるバックエンドのAPI呼び出しと、APIから返されるJSONの内容が正しく画面に表示されるかどうかを検証していると言っても過言ではないと思いますが、これはフロントエンドのユニットテストで担保できる内容です。 E2Eテストとフロントエンドのユニットテストはカバーする範囲が重複してしまっているのです。
ですので、画面項目の表示など、フロントエンドのユニットテストで担保できる部分は省略して、ユースケースから想定される一連のAPI呼び出しをテストシナリオとして用意し実行することで、システム全体の機能の品質を担保するということは理にかなっています。 そして、フロントエンドのUI変更の影響を受けにくくすることができます。 また、この構成において、領域毎にユニットテストをしっかり行っているのであれば、フロントエンドの操作までを自動化するE2Eテストでしか検出できないバグが混在する可能性は低いでしょう。 ですので、フロントエンドの操作を自動化せずとも、ユースケースから作成したテストシナリオに応じたHTTPリクエストだけをRESTfulなAPI群に対して発行してくれるテストランナーがあれば大変便利です。
ご存知の方も多いと思いますが、Karateというテストスイートがこのニーズにピッタリと合致します。
Test Automation Made Simple https://github.com/karatelabs/karate 2,048 forks. 8,929 stars. 4 open issues. Recent commits: fix(core): the json keyword parses a string RHS without walking embedded expressions'##(subSchema)' in a schema file was inlined at load time, so a nullablenested object could not be expressed once the schema lived in its own .jsonfile – the same trap that a bracket-less def has, and the case the earliermatch-RHS fix did not cover.v1 drew the line at the keyword, not the marker: evalAndCastTo's JSON branchcalled getValueAndForceParsingAsJson, which for a STRING only ran fromJson.Embedded expressions were walked for the json keyword solely when the RHS wasa JSON or XML literal, through evalKarateExpression's isJson / isXml branch.v2's executeJson parsed AND THEN walked unconditionally, which consumed the'##' and left the field plain required.Drop the trailing walk. A literal or doc-string RHS still gets one insideevalKarateExpression, so templates are untouched, while a string parsed here -typically karate.readAsString() of a schema file – is kept exactly as authoredand both '#(…)' and '##(…)' survive to the match engine. That makes'json schema = karate.readAsString(path)' the JSON twin of the 'text' then'xml' idiom already used for XML schemas, and it restores v1 parity for theother shapes the walk was reaching too: $-jsonpath, get, call and Node.Also honour step.getDocString() in executeJson. It was the only typed keywordignoring it, so 'json foo =' followed by a doc string silently bound null.Scope worth knowing: this covers the string-shaped path. A schema arriving asa Map – from karate.callSingle, say – still gets the lenient walk at theevalKarateExpression callsite and still inlines.fixes #3006Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> , Peter Thomas fix(core): arrays in the display serializer, cycle-safe deepCopy, callSingle disk cache refuses non-JSON resultsContinuing the sweep for the shared-mutable-reference / unchecked-array-castpattern behind #3014, two more real defects and one accepted limitation:- there are two JSON serializers in the codebase, and only one of them was fixed. StringUtils.formatJson — behind karate.pretty, karate.log, the HTTP request/response log and log masking — had no array branch at all, so every array fell through to o.toString() and rendered as "[B@1a2b3c" where the json-smart writers rendered [1,2,3]. Arrays now format as JSON arrays, element by element, and a test pins the two serializers' output equal across all eight primitive types, Object[], nulls, empties and nesting.- the callSingle disk cache is written with that same serializer, so a byte[] in the result (a certificate or keystore read from a file, raw response bytes) was persisted as its JVM identity string and handed back to the next JVM's warm run as that garbage string: cold run bytes, warm run string, no failure anywhere. The write is now gated on the result surviving a JSON round-trip — arrays, XML, Sets, POJOs, cycles, NaN/Infinity and non-String map keys keep the result out of the cache, with a warning naming the path, and the warm run re-executes instead. A truncated cache file also no longer fails every scenario: parse failures are RuntimeExceptions, which the IOException-only catch let through.- deepCopy carried a documented limitation: a self-referential container overflowed the stack, and two paths onto one list were copied into two independent lists. It now threads a lazily allocated identity memo, so cycles terminate and a value reached twice stays one value — for Maps, Lists, Sets, arrays (primitive included) and XML nodes alike. Object arrays copy into an Object[] shell and are retyped only when every element stays assignable AND nothing else took the shell; retyping a copy something else already holds would hand out two different arrays.Found by review: the widening path originally left back-references pointing ata stale narrow array, and the round-trip guard originally passed cycles thatthe writer renders as the string "[Circular]".fixes #3014Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> , Peter Thomas build(deps): bump github/codeql-action/upload-sarif from 4.37.6 to 4.37.7 (#3020)Bumps [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) from 4.37.6 to 4.37.7.- [Release notes](https://github.com/github/codeql-action/releases)- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)- [Commits](https://github.com/github/codeql-action/compare/5595ccaf912efad79be6eef63a5619ff05969be3…ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd)—updated-dependencies:- dependency-name: github/codeql-action/upload-sarif dependency-version: 4.37.7 dependency-type: direct:production update-type: version-update:semver-patch…Signed-off-by: dependabot[bot] <support@github.com>Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> , GitHub build(deps): bump github/codeql-action/analyze from 4.37.6 to 4.37.7 (#3019) [no ci]Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.6 to 4.37.7.- [Release notes](https://github.com/github/codeql-action/releases)- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)- [Commits](https://github.com/github/codeql-action/compare/5595ccaf912efad79be6eef63a5619ff05969be3…ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd)—updated-dependencies:- dependency-name: github/codeql-action/analyze dependency-version: 4.37.7 dependency-type: direct:production update-type: version-update:semver-patch…Signed-off-by: dependabot[bot] <support@github.com>Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> , GitHub build(deps): bump github/codeql-action/init from 4.37.6 to 4.37.7 (#3018) [no ci]Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.6 to 4.37.7.- [Release notes](https://github.com/github/codeql-action/releases)- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)- [Commits](https://github.com/github/codeql-action/compare/5595ccaf912efad79be6eef63a5619ff05969be3…ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd)—updated-dependencies:- dependency-name: github/codeql-action/init dependency-version: 4.37.7 dependency-type: direct:production update-type: version-update:semver-patch…Signed-off-by: dependabot[bot] <support@github.com>Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> , GitHub
KarateはGherkinという自然言語に近いフォーマットで書かれたテストシナリオを実行します。 KarateもいわゆるCucumberの一種なのですが、必要なステップ定義があらかじめ提供されているため、テストのためのプログラミングが必要ないというのが大きな利点です。 多少のHTTPの知識があれば、プロダクトオーナーや、受け入れをして品質をチェックするマネージャー、QAエンジニア、1年目の若手エンジニアもテストシナリオを作成することができます。 テスト対象のAPIの仕様はプロジェクト内部で共有されているでしょうから、あとは必要なAPIを呼び出し、結果の整合性を確認することに注力すれば良いのです。
例えば、 ・ログインする ・商品を探す ・特定の商品をカートに入れる ・カートを決済する
というユースケースは、4つの連続したAPI呼び出しであるケースが多いと思いますが、 HTTPのステータスコードやレスポンスボディ、レスポンスヘッダなどが期待値と一致するかどうかを検証することで、フロントエンドを含めたE2Eテストと同程度のテストを実施することが可能です。 API呼び出しのレスポンスヘッダーに含まれる値を後続のAPI呼び出しのリクエストボディに含めるといったことも、 もちろん可能ですので、実際のユースケースに適応させることは難しくありません。
ただし、ユースケースからテストシナリオを作ること自体はプロジェクトに関わるメンバーであれば開発者以外でも可能かもしれませんが、それをKarate用のGherkin(実際はDSL)として正しく書くのは、慣れるまでに少し時間と労力が必要です。
そこで、この記述ができるだけ簡単にできるように、「Karate Gherkin Visual Editor」をリリースしました。
https://krt.mukei-soft.co.jp/
必要な項目を入力するだけで、本格的なテストシナリオを記述することができます。 ブラウザがあれば利用でき、IDEなどのインストールは一切不要ですから、誰でもシナリオテストを記述することができるようになります。
次回は、「Karate Gherkin Visual Editor」の機能を細かくご紹介していきたいと思います。 それでは!
In English
Are you Practicing E2E test? The evolution of test suites for web fronts and native apps, including Selenium, has been remarkable these days, and many projects have already adopted it. Also, with the advent of headless web test automation tools such as Puppeteer, it is becoming less difficult to perform E2E tests on CI servers. On the other hand, since it is the fate of the front end that UI improvements are frequently made and AB tests are performed, the E2E test created with much effort may not work, and the man-hours for repairing it may be troublesome. I think there are many. If the E2E test created just before the release fails, you have to check whether it is a specification change or a degraded. Stopping the release and fixing the test if necessary is something you don’t want to do too much. If the decision is made to tolerate test failures and release, the E2E test is likely to have a sad future that will be abolished in the near future.
By the way, it is a common tendency these days to eliminate the functional difference between PC-Web, SP-Web, and native apps, and to provide RESTful APIs that do not depend on the front end for the back end. The front end calls the same API to provide the same functionality on any device. In this case, the screen operation automated by the E2E test verifies that the backend API call issued along with it and the JSON content returned from the API are displayed correctly on the screen. I think it’s no exaggeration to say that this is something that can be guaranteed by front-end unit tests. The E2E test and the front-end unit test have overlapping coverage.
Therefore, by omitting the parts that can be guaranteed by the front-end unit test such as the display of screen items, and preparing and executing a series of API calls assumed from the use case as a test scenario, the quality of the function of the entire system It makes sense to secure. And you can make it less susceptible to front-end UI changes. Also, in this configuration, if unit tests are performed firmly for each area, it is unlikely that bugs that can only be detected by the E2E test that automates the operation of the front end will be mixed. Therefore, it is very convenient to have a test runner that issues only HTTP requests according to the test scenario created from the use case to the RESTful API group without automating the operation of the front end.
As many of you may know, the Karate test suite fits this need perfectly.
Test Automation Made Simple https://github.com/karatelabs/karate 2,048 forks. 8,929 stars. 4 open issues. Recent commits: fix(core): the json keyword parses a string RHS without walking embedded expressions'##(subSchema)' in a schema file was inlined at load time, so a nullablenested object could not be expressed once the schema lived in its own .jsonfile – the same trap that a bracket-less def has, and the case the earliermatch-RHS fix did not cover.v1 drew the line at the keyword, not the marker: evalAndCastTo's JSON branchcalled getValueAndForceParsingAsJson, which for a STRING only ran fromJson.Embedded expressions were walked for the json keyword solely when the RHS wasa JSON or XML literal, through evalKarateExpression's isJson / isXml branch.v2's executeJson parsed AND THEN walked unconditionally, which consumed the'##' and left the field plain required.Drop the trailing walk. A literal or doc-string RHS still gets one insideevalKarateExpression, so templates are untouched, while a string parsed here -typically karate.readAsString() of a schema file – is kept exactly as authoredand both '#(…)' and '##(…)' survive to the match engine. That makes'json schema = karate.readAsString(path)' the JSON twin of the 'text' then'xml' idiom already used for XML schemas, and it restores v1 parity for theother shapes the walk was reaching too: $-jsonpath, get, call and Node.Also honour step.getDocString() in executeJson. It was the only typed keywordignoring it, so 'json foo =' followed by a doc string silently bound null.Scope worth knowing: this covers the string-shaped path. A schema arriving asa Map – from karate.callSingle, say – still gets the lenient walk at theevalKarateExpression callsite and still inlines.fixes #3006Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> , Peter Thomas fix(core): arrays in the display serializer, cycle-safe deepCopy, callSingle disk cache refuses non-JSON resultsContinuing the sweep for the shared-mutable-reference / unchecked-array-castpattern behind #3014, two more real defects and one accepted limitation:- there are two JSON serializers in the codebase, and only one of them was fixed. StringUtils.formatJson — behind karate.pretty, karate.log, the HTTP request/response log and log masking — had no array branch at all, so every array fell through to o.toString() and rendered as "[B@1a2b3c" where the json-smart writers rendered [1,2,3]. Arrays now format as JSON arrays, element by element, and a test pins the two serializers' output equal across all eight primitive types, Object[], nulls, empties and nesting.- the callSingle disk cache is written with that same serializer, so a byte[] in the result (a certificate or keystore read from a file, raw response bytes) was persisted as its JVM identity string and handed back to the next JVM's warm run as that garbage string: cold run bytes, warm run string, no failure anywhere. The write is now gated on the result surviving a JSON round-trip — arrays, XML, Sets, POJOs, cycles, NaN/Infinity and non-String map keys keep the result out of the cache, with a warning naming the path, and the warm run re-executes instead. A truncated cache file also no longer fails every scenario: parse failures are RuntimeExceptions, which the IOException-only catch let through.- deepCopy carried a documented limitation: a self-referential container overflowed the stack, and two paths onto one list were copied into two independent lists. It now threads a lazily allocated identity memo, so cycles terminate and a value reached twice stays one value — for Maps, Lists, Sets, arrays (primitive included) and XML nodes alike. Object arrays copy into an Object[] shell and are retyped only when every element stays assignable AND nothing else took the shell; retyping a copy something else already holds would hand out two different arrays.Found by review: the widening path originally left back-references pointing ata stale narrow array, and the round-trip guard originally passed cycles thatthe writer renders as the string "[Circular]".fixes #3014Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> , Peter Thomas build(deps): bump github/codeql-action/upload-sarif from 4.37.6 to 4.37.7 (#3020)Bumps [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) from 4.37.6 to 4.37.7.- [Release notes](https://github.com/github/codeql-action/releases)- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)- [Commits](https://github.com/github/codeql-action/compare/5595ccaf912efad79be6eef63a5619ff05969be3…ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd)—updated-dependencies:- dependency-name: github/codeql-action/upload-sarif dependency-version: 4.37.7 dependency-type: direct:production update-type: version-update:semver-patch…Signed-off-by: dependabot[bot] <support@github.com>Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> , GitHub build(deps): bump github/codeql-action/analyze from 4.37.6 to 4.37.7 (#3019) [no ci]Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.6 to 4.37.7.- [Release notes](https://github.com/github/codeql-action/releases)- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)- [Commits](https://github.com/github/codeql-action/compare/5595ccaf912efad79be6eef63a5619ff05969be3…ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd)—updated-dependencies:- dependency-name: github/codeql-action/analyze dependency-version: 4.37.7 dependency-type: direct:production update-type: version-update:semver-patch…Signed-off-by: dependabot[bot] <support@github.com>Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> , GitHub build(deps): bump github/codeql-action/init from 4.37.6 to 4.37.7 (#3018) [no ci]Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.6 to 4.37.7.- [Release notes](https://github.com/github/codeql-action/releases)- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)- [Commits](https://github.com/github/codeql-action/compare/5595ccaf912efad79be6eef63a5619ff05969be3…ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd)—updated-dependencies:- dependency-name: github/codeql-action/init dependency-version: 4.37.7 dependency-type: direct:production update-type: version-update:semver-patch…Signed-off-by: dependabot[bot] <support@github.com>Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> , GitHub
Karate runs a test scenario written in a format close to natural language called Gherkin. Karate is also a kind of so-called Cucumber, but it has the big advantage that it does not require programming for testing because the necessary step definitions are provided in advance.
With some HTTP knowledge, product owners, managers who accept and check quality, QA engineers, and young engineers in the first year can also create test scenarios. The specifications of the API to be tested will be shared within the project, so all you have to do is call the required API and focus on checking the consistency of the results.
For example ・log in ・find a product ・add a specific item to the cart ・settle the cart
In this use case, I think there are many cases where there are four consecutive API calls, By verifying whether the HTTP status code, response body, response header, etc. match the expected value, It is possible to carry out the same level of testing as the E2E test including the front end. It is also possible to include the value contained in the response header of the API call in the request body of the subsequent API call. Of course it is possible, so it is not difficult to adapt it to the actual use case.
However, creating a test scenario from a use case may be possible for non-developers as long as it is a member involved in the project, but writing it correctly as Gherkin for Karate (actually DSL) is a little until you get used to it. It takes time and effort.
Therefore, we have released “Karate Gherkin Visual Editor” to make this description as easy as possible.
https://krt.mukei-soft.co.jp/
You can write a full-scale test scenario just by entering the required items. Anyone can write test scenario because it can be used with a browser and no IDE is required.
Next time, I would like to introduce the functions of “Karate Gherkin Visual Editor” in detail. See you!