Testland
Browse all skills & agents

karate-testing

Authors Karate `.feature` files using its Gherkin-flavored DSL for HTTP API tests, leverages the `match` keyword with fuzzy validators (#number / #string / #regex / contains / arrays), runs the suite via JUnit 5 plus Maven Surefire, and produces JUnit XML for CI gating. Use when the project is on the JVM and prefers a feature-file authoring flow over Java-DSL fluent chains; for those fluent chains use restassured-testing, for the same YAML-style flow on a Python/pytest stack use tavern-testing.

Install with skills.sh (any agent)

npx skills add testland/qa --skill karate-testing
View source

karate-testing

Overview

Karate authors HTTP API tests as Gherkin-flavored .feature files with Given / When / Then steps, but unlike Cucumber the steps are executable directly - no glue code is required (karate-readme (opens in new window), karate-docs (opens in new window)).

This skill covers the API-testing slice of Karate. Its mock-server and UI-automation modes are out of scope here.

When to use

  • The team wants feature-file-based authoring (Gherkin shape) without step-definition glue code (the Cucumber pain point).
  • Multi-language test teams: Karate's DSL is approachable for non- Java developers (the .feature file IS the test; no Java to read).
  • API + mock + perf in one tool - Karate's distinguishing feature is cross-mode reuse of feature files.
  • Teams that need built-in JSON path + schema-fuzzy matching with one keyword (match).

If the team is already deep in REST Assured fluency, evaluate restassured-testing. For non-JVM stacks, use postman-collections or tavern-testing.

Install

Maven (consult karate-docs (opens in new window) for the current version - Karate publishes regularly under group io.karatelabs, artifacts karate-core and karate-junit5):

<dependency>
    <groupId>io.karatelabs</groupId>
    <artifactId>karate-junit5</artifactId>
    <version>${karate.version}</version>
    <scope>test</scope>
</dependency>

Pin ${karate.version} to a specific release listed on the GitHub release page (karate-readme (opens in new window)). Do not float on LATEST - DSL keywords have evolved across major versions.

Authoring

Feature file shape

Each .feature file lives under src/test/java/<package>/ (yes, under java/ even though it's not Java - Maven's resource conventions). Per karate-docs (opens in new window):

Feature: Simple API test example

  Scenario: Get user details
    Given url 'https://api.example.com'
    When method GET
    Then status 200
    And match response == { id: '#number', name: '#string', email: '#string', active: true }

The full step-keyword table (url, path, header, param, request, method, status, match) and its purpose column are in references/match-reference.md.

The match keyword

match is Karate's superpower - fuzzy structural matching on JSON or XML (karate-docs (opens in new window)):

# Type fuzzy: any number, any string
And match response == { id: '#number', name: '#string' }

# Array contains
And match response.items contains { sku: 'SKU-123' }

# Optional / nullable fields
And match response == { id: '#number', deletedAt: '##string' }   # ## = optional

The full match-modifier vocabulary (#boolean, #array, #object, #null, #notnull, #present, #notpresent, #regex <pattern>, ##<type>) and more examples are in references/match-reference.md.

Background block

For shared setup across scenarios:

Feature: Orders API

  Background:
    Given url 'https://api.example.com'
    And header Authorization = 'Bearer ' + karate.properties['api.token']

  Scenario: Get an order
    Given path 'orders/42'
    When method GET
    Then status 200
    And match response.order_id == 42

  Scenario: Create an order
    Given path 'orders'
    And request { sku: 'SKU-1', qty: 2 }
    When method POST
    Then status 201
    And match response == { order_id: '#number', sku: 'SKU-1', qty: 2 }

karate.properties['api.token'] reads a system property (passed via mvn test -Dapi.token=...) - the canonical pattern for secret injection without hard-coding.

Variables and chaining

Scenario: Create then read
  Given path 'orders'
  And request { sku: 'SKU-1', qty: 2 }
  When method POST
  Then status 201
  * def newId = response.order_id

  Given path 'orders/' + newId
  When method GET
  Then status 200
  And match response.sku == 'SKU-1'

* def name = expr declares a variable usable in subsequent steps. * is a step prefix (synonym of Given/When/Then - Karate keywords are documentation only when not asserting).

Running via JUnit 5

Karate ships a JUnit 5 runner that picks up .feature files adjacent to a Java runner class. Convention:

package com.example.api;

import com.intuit.karate.junit5.Karate;

class OrdersApiRunner {

    @Karate.Test
    Karate orders() {
        return Karate.run("orders").relativeTo(getClass());
    }
}

Karate.run("orders") resolves to orders.feature next to the runner. For multi-feature suites, add more @Karate.Test methods or use Karate.run().relativeTo(getClass()) to pick up every .feature in the same directory.

Run via Maven:

mvn test -Dapi.token=$API_TOKEN

(Per karate-docs (opens in new window).)

Reporting

Karate emits Cucumber-style HTML reports under target/karate-reports/, plus JUnit XML under target/karate-reports/karate-summary.xml. The JUnit XML is canonical for CI ingestion (same shape as Newman / REST Assured / JUnit's own *Test.java output).

CI integration

# .github/workflows/karate.yml
name: api-tests

on:
  pull_request:
  push:
    branches: [main]

jobs:
  karate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '21'
          cache: 'maven'

      - name: Run Karate suite
        env:
          API_TOKEN: ${{ secrets.STAGING_API_TOKEN }}
        run: mvn -B test -Dapi.token=$API_TOKEN

      - name: Upload Karate reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: karate-reports
          path: target/karate-reports/
          retention-days: 14

      - name: Surface JUnit results
        if: always()
        uses: dorny/test-reporter@v1
        with:
          name: Karate API tests
          path: target/karate-reports/karate-summary.xml
          reporter: java-junit

Anti-patterns

Anti-patternWhy it failsFix
Hard-coded URLs and tokens in .feature filesTests bind to one environment; secrets leak.Use karate.properties['api.url'] and karate.properties['api.token'].
match response == ... with literal IDs from a non-deterministic sourceTest fails on every run because the ID changes.Use '#number' instead of a literal; OR capture the ID with * def id = response.id and assert on it explicitly.
Combining UI + API + mock in one FeatureWhen one mode breaks, the unrelated assertions become noise.One mode per Feature; cross-link with * call read('helper.feature').
Missing Background for shared authEvery Scenario duplicates auth steps; rotation pain.Pull auth into Background.
Skipping --bail equivalent in CIA failed scenario doesn't stop subsequent scenarios; long failure logs.Karate doesn't bail mid-Feature by design; for fast-fail, run each Feature in its own JUnit class and use mvn -fae or -Dsurefire.failIfNoTests=false patterns.

Limitations

  • JVM only. Karate runs on the JVM; non-JVM teams should look at tavern-testing (Python) or postman-collections (Node).
  • DSL learning curve for non-Gherkin users. The match keyword's modifier vocabulary takes a session to internalize.
  • .feature files under src/test/java. Maven convention is surprising for first-timers; misplacing them under src/test/resources breaks the JUnit 5 runner's auto-discovery.
  • Cucumber-style reports are HTML-heavy. Combine with the karate-summary.xml for CI gating; the HTML is for human triage, not machine consumption.

References

  • karate-readme (opens in new window) - main repo: positioning, dependency name, release cadence.
  • karate-docs (opens in new window) - canonical DSL reference: feature-file syntax, match keyword, scenario / background / scenario outline, JUnit 5 runner pattern.
  • postman-collections - JSON-driven alternative.
  • restassured-testing - Java fluent-DSL alternative on the same JVM stack.

Karate keyword and match-modifier reference

View source (opens in new window)

Karate keyword and match-modifier reference

Companion to the karate-testing skill: the full step-keyword table and the complete match modifier vocabulary. Per karate-docs (opens in new window).

Step keywords

KeywordPurpose
Feature:One per file; describes the API surface under test.
Scenario:One per case; mirrors a single user / API journey.
Background:Steps that run before each Scenario (auth, base URL setup).
Given url '...'Set the request URL.
Given path '...'Append a path segment to the URL.
Given header X = YSet a request header.
Given param X = YSet a query parameter.
Given request {...}Set the JSON body.
When method <verb>Issue the HTTP request: get, post, put, delete.
Then status NAssert the response status code.
And match exprAssert response body / header / variable shape.

Match examples

# Equality on the whole response
And match response == { id: 1, name: 'Alice' }

# Type fuzzy: any number, any string
And match response == { id: '#number', name: '#string' }

# Regex constraint
And match response.email == '#regex .*@example\\.com'

# Array contains
And match response.items contains { sku: 'SKU-123' }

# Array length / shape
And match response.items == '#array'
And match response.items[0] == { sku: '#string', qty: '#number' }

# Optional / nullable fields
And match response == { id: '#number', deletedAt: '##string' }   # ## = optional

Match modifiers

#number, #string, #boolean, #array, #object, #null, #notnull, #present, #notpresent, #regex <pattern>, and ##<type> for optional compose into a small but expressive matcher language (karate-docs (opens in new window)).

Related skills

api-chaos-runner

Runs the project's existing API tests under injected network chaos - latency, timeouts, dropped connections, bandwidth caps, packet loss - via Toxiproxy (notes on Pumba / Gremlin / LitmusChaos). Builds a per-scenario chaos matrix and reports which assertions break under which conditions, verifying resilience patterns (retry, circuit-breaker, timeout, fallback). Unlike schemathesis-fuzzing and restler-fuzzing, which generate new tests from a schema, this drives your EXISTING example-based suite.

api-testing-overview

Teaches API testing from zero: what functional API testing covers, how it differs from contract testing and load testing, and a decision table that picks one tool from observable project facts (language and build file, whether an OpenAPI or GraphQL schema exists, functional vs spec-conformance fuzzing vs stateful security fuzzing, whether non-engineers read the tests). Names the real options (Postman with newman, REST Assured, Karate, Tavern, Schemathesis, RESTler), gives install and first-run commands with what a passing run looks like, and the traps that bite first: asserting only on HTTP status, order-dependent tests sharing server state, and hardcoded environment URLs and secrets. Use when an HTTP API needs automated tests and no tool has been chosen, or when an inherited suite only checks status codes.

postman-collections

Authors Postman collections (requests + tests + variables + environments), runs them headless via the Newman CLI, configures reporters (cli / json / junit / html) for CI artifact upload, and uses iteration data files (JSON / CSV) for data-driven runs. Use when the project ships HTTP API tests authored in Postman and the team needs CI execution alongside or instead of the Postman desktop runner.

restassured-testing

Authors REST Assured (Java) API tests using the given().when().then() BDD-style DSL - status code + JSON/XML path assertions + authentication (Basic, OAuth2, API key). Configures Maven / Gradle dependencies, runs via JUnit 5, and emits Surefire / JaCoCo reports for CI gating. Use when the project is on the JVM and wants type-safe API tests in the app's own language; for a Gherkin feature-file flow on the same JVM use karate-testing, for YAML tests on the pytest stack use tavern-testing.

restler-fuzzing

Runs stateful REST API fuzzing using Microsoft's RESTler - infers producer-consumer dependencies from an OpenAPI spec, drives sequences of requests (POST → GET → DELETE chains), and reports 5xx errors, resource leaks, and hierarchy violations. Wraps the canonical 4-stage workflow (compile → test → fuzz-lean → fuzz). Use when the API is stateful (resources are created, queried, modified, deleted) and Schemathesis's stateless fuzzing is missing the multi-step bugs.

schemathesis-fuzzing

Generates property-based API tests automatically from an OpenAPI 2/3.x or GraphQL schema using Schemathesis, runs them via the `schemathesis run` CLI or as a pytest decorator, configures the canonical checks (status_code_conformance, response_schema_conformance, content_type_conformance, response_headers_conformance, not_a_server_error), and gates CI on schema-conformance failures plus 5xx detection. Use when the project ships an OpenAPI or GraphQL schema and the team wants schema-driven coverage that scales as the API evolves.

tavern-testing

Authors Tavern API tests as YAML files (`test_*.tavern.yaml`) with `test_name` + `stages` + `request` + `response` blocks, runs them through the Tavern pytest plugin (auto-discovered), and gates CI on the resulting JUnit XML. Covers RESTful, MQTT, and gRPC variants. Use when the project runs on pytest and prefers YAML over a Python- or Java-DSL; on the JVM use karate-testing or restassured-testing instead, and for schema-driven property-based fuzzing on the same pytest stack use schemathesis-fuzzing.