みなさん、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,032 forks. 8,869 stars. 15 open issues. Recent commits: fix: cycle-safe JsObject.getEntries — no SOE on self-referential vars (fixes #2887)LinkedHashSet.add hashes each Entry (key^value); when a scenario variableholds a self-referential Map (`* def obj = {}; * eval obj.self = obj`),value.hashCode() recurses through AbstractMap.hashCode forever.Triggered whenever the bindings map is walked through Bindings.entrySet():afterScenario hook → karate.call → inheritVariables → getAllVariables →new LinkedHashMap<>(bindings) → bindings.entrySet() → boom.Back the entry set with a LinkedHashMap and return its entrySet() instead.LinkedHashMap.put hashes keys only (Strings — safe), and the input isalready unique-by-key, so the LinkedHashSet's dedupe-by-hash was wastedwork even in the common case. Companion to 2b2947a (cycle-safe Jsonserialization). , Peter Thomas js: parser accepts keywords as get/set accessor property namesobject_elem's accessor branch advertised keywords in T_ACCESSOR_KEY_STARTbut the consume logic only accepted IDENT/strings/numbers, so getterslike `get return() {}` / `get default() {}` failed parse with`expected: [IDENT, S_STRING]`. Switch the consume to `anyOf(…)` overthe same set the lookahead uses. Fixes 3 test262 paths (reserved-wordsident-name accessors, for-of iterator-close-throw-get-method); 4 siblingtests in the same cluster now reach runtime and fail on the separateIteratorClose-on-throw residual.TEST262.md: drop misdiagnosed "([a = expr()] = it)" entry that pointedat array-literal mis-routing — root cause was unrelated. Note classsyntax stays parked because LLM-emitted code is scripting / glue, notOO application code.Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> , Peter Thomas docs(IMAGE_SPIKE): trim status / history log; tighten cold-read seedThe doc had accumulated detailed work-log content from Phase 1 + Phase1b — useful in-flight, noise for an LLM coming in cold. Forward design(decisions D1-D21, architecture §3, Phase 2/3/4/5 scope, RELEASING.mdamendments, open questions) all kept verbatim.Trims: – Vocabulary note (D17): drop past-tense "landed" prose, keep just the forward vocabulary facts. – §1 Goal #1: drop "see Phase 1/1b sections for what landed where" tail. – §4 Phase 1: drop the "Phase 1 vs Phase 1b" navigation note + tighten the status callout to one line. Matrix kept (decision provenance). – §4 Phase 1b: collapse "Done so far" (5 detailed bullets) + "Scope amendments made mid-build" + "Data plumbing" + "Exit criteria" into one summary line. Deferred items kept verbatim (forward). – §4 Phase 2: drop the strikethrough D17 line; surfaced as a brief status note above the remaining Phase 2 scope. – §6 O8 + O20: remove resolved-and-closed rows. – §6 O16: reword from "atomic rollout coming up" to "karate-core landed; sibling repo + Rust launcher still outstanding". – §8 Quick reference: drop the ✅ stack of landed files (any code clone shows it). Keep the 🔨 forward layout for Phases 3+4 + the user-project shape at the end of the spike.699 → 656 lines.Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> , Peter Thomas docs: state the ENTER-events-are-thin rule explicitly in DESIGN.mdThe principle that only FEATURE_EXIT carries the heavy payload (fullFeatureResult.toJson) was implicit in the JSONL stream section —inferrable from the field lists + the STEP/HTTP filter note + the"embeds only at FEATURE_EXIT" paragraph. Call it out up front as aload-bearing rule so future event-type additions honour the splitinstead of denormalising step/embed data onto envelope events.Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> , Peter Thomas core: OUTLINE_ENTER event + collapse CoverageAtom into scenario fieldsCoverageAtom mixed three concerns: slug computation, a "taxonomy atom"emission format, and tag-text extraction — all bundled because thekarate-openapi consumer wanted a normalised tree on the wire. Thechain/atom abstraction was overproduced: most of what it emitted waseither already on FEATURE_ENTER (feature name/description/tags) or wasdenormalised parent-outline metadata replicated onto every example.Restructured so: – New RunEventType.OUTLINE_ENTER + OutlineRunEvent (record). Fires once per outline section before its first generated example runs, carrying outline name/description/tags/numExamples/line. No OUTLINE_EXIT — completion is implied by the last outline-example's SCENARIO_EXIT. – FeatureRuntime fires from the single-threaded iteration loops (both sequential and parallel paths), tracking outlineEnteredSections so OUTLINE_ENTER fires at most once per section. – Slug + tag-text helpers moved to RunUtils (new class). FeatureRunEvent, ScenarioRunEvent, OutlineRunEvent all import. – ScenarioRunEvent.toJson() inlines description (redacted under @report=false) and, for outline-examples, outlineSlug + isOutlineExample + exampleIndex. coverageItems[] field deleted. – FeatureRunEvent.toJson() adds slug for symmetry with scenario events. – CoverageAtom.java deleted (206 LOC). CoverageAtomTest -> RunUtilsTest, atom-shape tests removed, slug + tag tests kept. – DESIGN.md event-lifecycle ASCII tree updated; JSONL stream example spelled out per-event-type with the actual data fields.Wire-shape change: receivers reading coverageItems[] (today only../karate-async/karate-openapi) must switch to reading the scenario JSONflat fields + the new OUTLINE_ENTER event. v2 hasn't GA'd; clean breakacceptable.2,223 tests pass (one new outline-firing test added).Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> , Peter Thomas
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,032 forks. 8,869 stars. 15 open issues. Recent commits: fix: cycle-safe JsObject.getEntries — no SOE on self-referential vars (fixes #2887)LinkedHashSet.add hashes each Entry (key^value); when a scenario variableholds a self-referential Map (`* def obj = {}; * eval obj.self = obj`),value.hashCode() recurses through AbstractMap.hashCode forever.Triggered whenever the bindings map is walked through Bindings.entrySet():afterScenario hook → karate.call → inheritVariables → getAllVariables →new LinkedHashMap<>(bindings) → bindings.entrySet() → boom.Back the entry set with a LinkedHashMap and return its entrySet() instead.LinkedHashMap.put hashes keys only (Strings — safe), and the input isalready unique-by-key, so the LinkedHashSet's dedupe-by-hash was wastedwork even in the common case. Companion to 2b2947a (cycle-safe Jsonserialization). , Peter Thomas js: parser accepts keywords as get/set accessor property namesobject_elem's accessor branch advertised keywords in T_ACCESSOR_KEY_STARTbut the consume logic only accepted IDENT/strings/numbers, so getterslike `get return() {}` / `get default() {}` failed parse with`expected: [IDENT, S_STRING]`. Switch the consume to `anyOf(…)` overthe same set the lookahead uses. Fixes 3 test262 paths (reserved-wordsident-name accessors, for-of iterator-close-throw-get-method); 4 siblingtests in the same cluster now reach runtime and fail on the separateIteratorClose-on-throw residual.TEST262.md: drop misdiagnosed "([a = expr()] = it)" entry that pointedat array-literal mis-routing — root cause was unrelated. Note classsyntax stays parked because LLM-emitted code is scripting / glue, notOO application code.Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> , Peter Thomas docs(IMAGE_SPIKE): trim status / history log; tighten cold-read seedThe doc had accumulated detailed work-log content from Phase 1 + Phase1b — useful in-flight, noise for an LLM coming in cold. Forward design(decisions D1-D21, architecture §3, Phase 2/3/4/5 scope, RELEASING.mdamendments, open questions) all kept verbatim.Trims: – Vocabulary note (D17): drop past-tense "landed" prose, keep just the forward vocabulary facts. – §1 Goal #1: drop "see Phase 1/1b sections for what landed where" tail. – §4 Phase 1: drop the "Phase 1 vs Phase 1b" navigation note + tighten the status callout to one line. Matrix kept (decision provenance). – §4 Phase 1b: collapse "Done so far" (5 detailed bullets) + "Scope amendments made mid-build" + "Data plumbing" + "Exit criteria" into one summary line. Deferred items kept verbatim (forward). – §4 Phase 2: drop the strikethrough D17 line; surfaced as a brief status note above the remaining Phase 2 scope. – §6 O8 + O20: remove resolved-and-closed rows. – §6 O16: reword from "atomic rollout coming up" to "karate-core landed; sibling repo + Rust launcher still outstanding". – §8 Quick reference: drop the ✅ stack of landed files (any code clone shows it). Keep the 🔨 forward layout for Phases 3+4 + the user-project shape at the end of the spike.699 → 656 lines.Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> , Peter Thomas docs: state the ENTER-events-are-thin rule explicitly in DESIGN.mdThe principle that only FEATURE_EXIT carries the heavy payload (fullFeatureResult.toJson) was implicit in the JSONL stream section —inferrable from the field lists + the STEP/HTTP filter note + the"embeds only at FEATURE_EXIT" paragraph. Call it out up front as aload-bearing rule so future event-type additions honour the splitinstead of denormalising step/embed data onto envelope events.Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> , Peter Thomas core: OUTLINE_ENTER event + collapse CoverageAtom into scenario fieldsCoverageAtom mixed three concerns: slug computation, a "taxonomy atom"emission format, and tag-text extraction — all bundled because thekarate-openapi consumer wanted a normalised tree on the wire. Thechain/atom abstraction was overproduced: most of what it emitted waseither already on FEATURE_ENTER (feature name/description/tags) or wasdenormalised parent-outline metadata replicated onto every example.Restructured so: – New RunEventType.OUTLINE_ENTER + OutlineRunEvent (record). Fires once per outline section before its first generated example runs, carrying outline name/description/tags/numExamples/line. No OUTLINE_EXIT — completion is implied by the last outline-example's SCENARIO_EXIT. – FeatureRuntime fires from the single-threaded iteration loops (both sequential and parallel paths), tracking outlineEnteredSections so OUTLINE_ENTER fires at most once per section. – Slug + tag-text helpers moved to RunUtils (new class). FeatureRunEvent, ScenarioRunEvent, OutlineRunEvent all import. – ScenarioRunEvent.toJson() inlines description (redacted under @report=false) and, for outline-examples, outlineSlug + isOutlineExample + exampleIndex. coverageItems[] field deleted. – FeatureRunEvent.toJson() adds slug for symmetry with scenario events. – CoverageAtom.java deleted (206 LOC). CoverageAtomTest -> RunUtilsTest, atom-shape tests removed, slug + tag tests kept. – DESIGN.md event-lifecycle ASCII tree updated; JSONL stream example spelled out per-event-type with the actual data fields.Wire-shape change: receivers reading coverageItems[] (today only../karate-async/karate-openapi) must switch to reading the scenario JSONflat fields + the new OUTLINE_ENTER event. v2 hasn't GA'd; clean breakacceptable.2,223 tests pass (one new outline-firing test added).Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> , Peter Thomas
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!