Q33 of 40 · Karate

How does Karate's parallel runner avoid shared-state issues, and where can it still break?

KarateSeniorkarateparallel-executionthread-safetyshared-stateconcurrency

Short answer

Short answer: Karate isolates Karate variables per thread — scenarios in the same feature always run sequentially on one thread; different features run concurrently. Shared state still breaks when tests read/write the same database rows, use the same external resource IDs, or register conflicting WireMock/Karate Mock stubs globally.

Detail

What Karate isolates automatically:

  • Each parallel thread gets its own karate object, variable scope, HTTP client, and cookie jar
  • Variables defined in one thread's feature cannot leak into another thread
  • Background runs fresh for every scenario on the assigned thread

What it does NOT isolate:

  1. Database state: two features create a user with the same email → 409 Conflict. Fix: UUID-prefix every resource name.
  2. Shared Karate Mock stubs: if mock stubs are registered globally and not scoped per test, thread A's stub can intercept thread B's request. Fix: start separate mock server instances per test class or scope stubs with conditional path matching.
  3. callonce / karate.callSingle: these intentionally share cached results across threads — if the cached result contains mutable state (a session ID that gets invalidated), it breaks in parallel.
  4. External rate limits: many parallel tests hitting a sandbox API simultaneously may trigger rate limiting. Fix: reduce thread count or add per-thread delays.
  5. File system writes: if tests write to files (logs, screenshots), shared file paths cause race conditions. Fix: use thread-ID or UUID-suffixed file names.

Detection strategy: run the suite in parallel and again sequentially. Compare failure patterns — failures that only occur in parallel are shared-state issues.

// EXAMPLE

parallel-safety.feature

Feature: Thread-safe test data creation

  Background:
    * url baseUrl
    * header Authorization = 'Bearer ' + bearerToken

  Scenario: Create and verify product (parallel-safe)
    # UUID prefix ensures no collision with other parallel threads
    * def uniqueSku   = 'SKU-' + karate.uuid().substring(0, 8)
    * def uniqueName  = 'Product-' + uniqueSku

    # Create unique product for this thread/scenario
    * def product = call read('classpath:helpers/create-product.feature')
        { name: uniqueName, sku: uniqueSku, price: 9.99 }

    # Assert on the specific product created by this thread
    Given path '/products/' + product.id
    When  method GET
    Then  status 200
    And   match response.sku  == uniqueSku    # specific to this thread's resource
    And   match response.name == uniqueName

    # Cleanup — remove this thread's product
    * call read('classpath:helpers/delete-product.feature') { id: product.id }

// WHAT INTERVIEWERS LOOK FOR

Understanding the feature-file unit of parallelism, what Karate isolates vs what it doesn't, and the UUID-prefix fix for database collision. The detect-by-comparing-parallel-vs-sequential strategy is a senior signal.

// COMMON PITFALL

Assuming that because Karate handles variable isolation the tests are automatically parallel-safe. Database state, mock stubs, and external rate limits are all outside Karate's isolation boundary.