skip to content

Karate covers API tests, a mock server and a Gatling load bridge in one tool. What does driving a load run from the same `.feature` file buy, and where does that one-tool model stop paying?

level: seniorimportance: should knowfreq 40%

answer

  1. Ask what is actually shared across the three
  2. The load run replays a functional feature file
  3. Assertions still evaluate under load
  4. An HTTP client is built per scenario
  5. Connection per iteration, not per virtual user

basics

~20 s

One artefact: karate-gatling points a simulation at a .feature file, so the functional scenario and its assertions become the load script. It stops paying because Karate builds an HTTP client per scenario, which under load opens a connection per iteration.

solid answer

~40 s

The buy is that there is no second script. `karate-gatling` takes a feature path — `karateFeature("classpath:features/cats-crud.feature")` — so the load run replays a scenario you already maintain, with its `match` steps intact, and a mock authored as a feature file starts from the test itself via `karate.start('cats-mock.feature')`. One language, one runtime, nothing to keep in sync. The cost is that a functional runtime is not a load client. Karate creates an HTTP client per scenario execution deliberately, so that scenarios stay isolated; under load that means a connection per iteration where a dedicated load tool keeps one per virtual user. Karate 2.x added an opt-in shared connection manager in `karate-gatling` for exactly this, and Karate 1.x has none. Assertions also run per virtual user, so client-side work sits on the measurement path.

code

gherkin · 16 lines
gherkin
Feature: cats CRUD

  Background:
    * url baseUrl

  Scenario: create and read a cat
    Given path 'cats'
    And request { name: 'Fluffy', age: 3 }
    When method post
    Then status 201
    * def catId = response.id

    Given path 'cats', catId
    When method get
    Then status 200
    And match response contains { name: 'Fluffy', age: 3 }

go deeper

for a junior

Know the shape: karate-gatling is given a feature path, so a scenario you already wrote becomes the load script, assertions and all.

for a middle

Explain what is actually shared - language, runtime and artefact type - and note that the mock is a different feature file rather than the same one.

for a senior

Lead with the client-per-scenario cost and the assertions on the measurement path. That is the difference between a smoke load run and capacity work.

for a principal

Decide by question, not by tool: consolidation is worth it for continuous load on the real suite, and not for capacity planning that needs a purpose-built generator.

## What "one tool" actually means here It is worth being exact, because the claim is often overstated. Karate does not run the *same file* as test, mock and load. What it shares is the language, the runtime and the artefact type: - **Test.** A `.feature` file with `url`, `path`, `method`, `status` and `match` steps. - **Load.** `karate-gatling` exists in both lines of the project and takes a feature path — `karateFeature("classpath:features/cats-crud.feature")` — so the load run replays a functional scenario you already have. - **Mock.** A mock is a different feature file, but it is a feature file, started from a test with `karate.start('cats-mock.feature')` or from a Java builder, in the same process. So the honest version of the claim is: **one language and one runtime across three jobs**, and one of the three genuinely reuses the other's artefact. ## What the shared artefact buys 1. **No second script to keep in sync.** The usual failure of load testing is a simulation that drifted away from the API months ago. A load run built on the functional scenario cannot drift, because it *is* the functional scenario. 2. **Assertions survive the trip.** The `match` steps still evaluate under load, so the run is not only measuring latency — it is also checking that the service still answers correctly at concurrency. A response that degrades into an error body shows up as a failure, not as a fast request. 3. **One skill for the team.** Whoever can read the API suite can read the load scenario. 4. **The mock is in the same language.** A team that already writes feature files does not learn a second stub syntax to stand up a dependency. ## Where it stops paying: the HTTP client This is the concrete answer, and it is the one that separates a real answer from an enthusiastic one. Karate builds an HTTP client **per scenario execution**. That is a deliberate functional-suite property — per-scenario isolation, so one scenario's configuration cannot reach another's in-flight request — and it is the wrong shape for a load generator, where you want a connection reused across iterations. The project measured it on a two-host bench and states it plainly in its own source: under Gatling, Karate opened **4,000 distinct client ports where plain Gatling kept 8**. Karate 2.x added an opt-in shared connection manager in `karate-gatling` to collapse that, worth roughly a quarter of Karate's per-iteration overhead on a plaintext endpoint and more against TLS, where the avoided handshake costs two round trips and asymmetric crypto. Karate 1.x has no such class. And the option is opt-in in 2.x rather than default precisely because a shared pool gives up the isolation a functional suite is buying. Two more limits belong in the same answer: - **Assertions sit on the measurement path.** Every `match` runs per virtual user, so assertion-heavy scenarios put client-side CPU into your numbers. A load scenario often wants a thinner assertion set than the functional one it came from. - **A failed step ends the iteration.** A failing assertion fails the scenario, so the remaining steps of that virtual user's iteration never execute. Under load that turns a partly-degraded service into a run whose later endpoints were barely exercised — which is exactly when you most wanted the numbers. ## The mock side of the same argument The mock reuse is the cleaner half of the story. Because the mock is a feature file started in process, a test can stand up its dependency and tear it down without a second product, a second config format or a second lifecycle to manage in CI. The limit is scope rather than fidelity: this is an in-process test double for your own suite, not a shared stub server other teams point at. When the need is a long-lived, separately-operated stub estate, that is a different tool's job and the one-tool argument does not reach it. ## How to decide | If you need | One tool is | Because | |---|---|---| | a smoke load run in CI on the real suite | a good fit | no second artefact, assertions intact | | capacity planning at high concurrency | a poor fit | client-per-scenario, assertions on the path | | an in-process double for your own tests | a good fit | same language, same lifecycle | | a stub estate other teams consume | out of scope | that is a standalone server's job | ## How to answer this in an interview Name the buy in one sentence — the load run replays the functional feature file, so there is no second script to drift — and then go straight to the cost, because that is what is being probed. The client-per-scenario point is the strongest thing you can say: it is a deliberate functional property, it is measurable, and the project's own mitigation is opt-in and version-specific. Finish by scoping the claim honestly: one language across three jobs, with one real artefact reuse, not one file doing everything.

  • Under a Karate load run, what happens to the rest of a scenario when one `match` fails?
    The step fails, the scenario fails, and the remaining steps of that iteration are not executed. Under load that skews the run: a service that starts returning errors mid-scenario leaves the later endpoints barely exercised, so the numbers you have for them are thin exactly when the service was worst.
  • Why is Karate's per-scenario HTTP client a problem only under load?
    Because in a functional suite it is the point. A client per scenario means one scenario's `configure` cannot disturb another's in-flight request, and that isolation is what a test suite is buying. Under load the same property costs you connection reuse, which is why sharing a connection manager is opt-in rather than the default.
  • Does the mock server make Karate a replacement for a standalone stub server?
    No. A Karate mock is a feature file started in your own process for your own suite's benefit, which is why it needs no second product or config format. A stub estate that other teams point at, operate and reset independently is a different job, and the one-tool argument does not stretch to it.

saying these in an interview costs you the question

  • Says the same file is simultaneously the test, the mock and the load script
  • Claims a functional runtime is as efficient as a dedicated load client
  • Forgets that assertions still execute under load, on every virtual user
  • Assumes connections are reused across iterations by default
  • Treats the in-process mock as a shared stub server for other teams