On this page14 sections

Karate API Automation Project Sample

A complete API automation project sample using Karate, feature files, reusable scenarios, authentication setup, schema matching, data-driven tests, HTML reporting, parallel execution, and CI/CD.

Beginner-to-intermediateAPI automationRegression testingSchema validationapi automation
Setup: 25–40 minutesLanguage: Java 17Framework: KaratePackage manager: MavenBest for: API testers

Overview

This Karate API automation project sample shows how to structure readable, maintainable API tests using Karate against Restful Booker (https://restful-booker.herokuapp.com), a free, widely-used API-testing playground by Mark Winteringham with authentication and full CRUD over hotel bookings. It uses Maven for dependencies, Gherkin-style feature files for scenarios, karate-config.js for environment configuration, reusable feature calls for setup flows — auth, test-data create, and cleanup — Karate's built-in match syntax for assertions and schema validation, parallel execution, and Karate HTML reports for evidence. The goal is API tests that are easy to read, easy to review, and CI-ready without a heavy Java framework around every call.

Project goals

  • Set up Karate with Maven (Karate BOM + JUnit 5) so that mvn test runs all feature files and generates the built-in HTML report without extra plugins
  • Write karate-config.js to load baseUrl and credentials per environment so feature files never contain hardcoded URLs or secrets
  • Organise feature files by resource (auth/, bookings/) with a separate reusable/ folder for shared flows that any feature can call via `call read()`
  • Build a reusable create-token.feature that calls POST /auth and returns the token; scenarios that need auth call it once per Scenario rather than duplicating HTTP steps
  • Authenticate write operations by sending the token as a Cookie header (cookie token = authToken) — not Authorization: Bearer, which Restful Booker rejects with 403
  • Implement full CRUD coverage for /booking: POST to create with a captured bookingid, GET to read back, PUT to update, PATCH to partial update, DELETE to remove (asserting 201, not 200/204)
  • Validate response schemas using Karate's built-in match syntax and fuzzy markers (#notnull, #string, #number) so tests assert response shape, not just status code
  • Use Scenario Outline with Examples tables and external JSON/CSV data files for data-driven booking scenarios that cover multiple payload variants in one feature
  • Run the suite in parallel using a ParallelTestRunner so the test suite scales and finishes quickly on CI
  • Generate the Karate HTML report at target/karate-reports/karate-summary.html and publish it as a GitHub Actions artifact on every run
  • Apply @smoke, @regression, and @wip tags so CI can target subsets without maintaining separate runner files
  • Write negative tests that assert actual Restful Booker behaviour — including responses where the API returns 500 or 200 for bad payloads — rather than assuming a tidy 4xx

Architecture

Feature-First: Features → Reusable Features → Runners → Config → Data / Schemas / Reports

Feature files are organised by API resource under src/test/resources/features/. Shared flows (auth, create/delete booking) live in a reusable/ subfolder and are invoked with `call read()`. karate-config.js at the resources root initialises environment config for every run. Two runner classes — KarateTestRunner for sequential runs and ParallelTestRunner for parallel — sit in src/test/java. Data payloads and CSV files go in data/; expected response shapes go in schemas/. Reports are generated by Karate into target/karate-reports/.

src/test/resources/karate-config.jsEnvironment-aware config: baseUrl, username, password per environment
src/test/resources/features/auth/Auth feature files: create-token.feature (POST /auth, returns token)
src/test/resources/features/bookings/Booking resource scenarios: create, get, update, delete, schema validation, data-driven
src/test/resources/features/reusable/Shared flows called by other features: create-token.feature, create-booking.feature, delete-booking.feature
src/test/resources/data/JSON payloads and CSV files used by Scenario Outline data-driven tests
src/test/resources/schemas/JSON schema files for match-based response shape validation
src/test/java/codes/qa/karate/runners/KarateTestRunner.java (sequential) and ParallelTestRunner.java (parallel execution)
target/karate-reports/Karate HTML report generated automatically after each run

Prerequisites

  • Java 17 or later installed and JAVA_HOME set
  • Maven 3.8 or later
  • Git
  • An IDE with Maven support (IntelliJ IDEA recommended for .feature file syntax highlighting)
  • Basic understanding of HTTP and REST: methods, status codes, request/response
  • Basic JSON: reading and writing JSON objects and arrays
  • Familiarity with common HTTP status codes (200, 201, 400, 401, 403, 404)
  • Optional: Postman or curl for manual API exploration; basic Gherkin familiarity helps but is not required

Folder structure

Project structure
Bash
pom.xml                                   # Maven build: Karate BOM, JUnit 5, Maven Surefire with JUnit Platform provider
src/test/resources/karate-config.js       # Environment-aware config: baseUrl, credentials, shared variables returned to every feature
src/test/resources/features/auth/create-token.feature  # Calls POST /auth and returns {token} for use in write-operation scenarios
src/test/resources/features/bookings/create-booking.feature  # POST /booking; captures bookingid for downstream CRUD and cleanup
src/test/resources/features/bookings/get-booking.feature  # GET /booking/{id} positive and negative tests; asserts flat response shape
src/test/resources/features/bookings/update-booking.feature  # PUT and PATCH /booking/{id}; asserts 200 and full/partial response shapes; requires Cookie auth
src/test/resources/features/bookings/delete-booking.feature  # DELETE /booking/{id}; asserts 201 Created (Restful Booker's documented delete response)
src/test/resources/features/bookings/schema-validation.feature  # Schema tests using Karate match and fuzzy markers for every booking endpoint
src/test/resources/features/bookings/data-driven-booking.feature  # Scenario Outline over multiple booking payloads from data/bookings.json or bookings.csv
src/test/resources/features/reusable/create-token.feature  # Shared auth flow called by scenarios that need write access; returns token variable
src/test/resources/features/reusable/create-booking.feature  # Creates a test booking and returns {bookingid}; called in Background or setup steps
src/test/resources/features/reusable/delete-booking.feature  # Deletes a booking by id; used as a cleanup step to keep Restful Booker tidy
src/test/resources/data/booking-payloads.json  # Valid and invalid booking payloads used by data-driven Scenario Outlines
src/test/resources/data/booking-data.csv  # CSV variant of booking data for Scenario Outline Examples table
src/test/resources/schemas/booking-schema.json  # Expected shape of a GET /booking/{id} response for schema match assertions
src/test/java/codes/qa/karate/runners/KarateTestRunner.java  # JUnit 5 @Karate.Class runner — discovers all features sequentially
src/test/java/codes/qa/karate/runners/ParallelTestRunner.java  # Karate parallel runner — executes features across threads; configure thread count here
.github/workflows/karate-tests.yml        # GitHub Actions: Java 17 setup, Maven cache, mvn test, upload karate-reports artifact
README.md                                 # Project setup, run commands, environment config, and portfolio guidance

Setup & run

Installation

  1. 1.Clone the repository once published: git clone <repo-url> && cd karate-api-automation
  2. 2.Verify tooling: java -version (expect 17+) and mvn -version (expect 3.8+)
  3. 3.Download dependencies: mvn dependency:resolve
  4. 4.Compile runner classes: mvn test-compile
  5. 5.Run a smoke check: mvn test -Dkarate.options="--tags @smoke"
  6. 6.Open the report: target/karate-reports/karate-summary.html in a browser

Commands

Run full suite

mvn test

Runs all .feature files via KarateTestRunner and generates the HTML report in target/karate-reports/

Run against a named environment

mvn test -Dkarate.env=staging

Sets karate.env to 'staging'; karate-config.js switches baseUrl and credentials accordingly

Run a single feature file

mvn test -Dkarate.options="classpath:features/bookings/create-booking.feature"

Targets one feature file for fast feedback when iterating on a scenario

Run by tag

mvn test -Dkarate.options="--tags @smoke"

Runs only scenarios tagged @smoke — useful for a quick sanity check before a full regression run

Exclude WIP scenarios

mvn test -Dkarate.options="--tags ~@wip"

Skips any scenario tagged @wip so in-progress work does not break CI

Run parallel

mvn test -Dtest=ParallelTestRunner

Executes all features across parallel threads; each scenario must own independent test data

Environment

VariableDescriptionExampleRequired
karate.envSelects the active environment in karate-config.js. Controls baseUrl and credentials. Pass via -Dkarate.env on the Maven command line or as a GitHub Actions env var.stagingNo
qa.baseUrlOverrides the default base URL in karate-config.js when set as a Java system property via -Dqa.baseUrl. Useful for pointing tests at a local mock or a staging environment.https://restful-booker.herokuapp.comNo
qa.usernameRestful Booker auth username read in karate-config.js via karate.properties['qa.username']. Set as a GitHub Actions secret in CI; default 'admin' applies locally.adminNo
qa.passwordRestful Booker auth password read in karate-config.js via karate.properties['qa.password']. Set as a GitHub Actions secret in CI; default 'password123' applies locally.password123No

Test data strategy

  • Every test that writes to Restful Booker (update, delete) creates its own booking in a Background block via call read('classpath:features/reusable/create-booking.feature') and uses the returned bookingid — no test shares an id with another
  • Cleanup: each scenario that creates a booking cleans up with call read('classpath:features/reusable/delete-booking.feature') in an * After or in the final step so Restful Booker stays tidy across runs
  • Auth: a reusable create-token.feature calls POST /auth once per Scenario and returns {token}; the token is passed to write operations as a Cookie — Given cookie token = authToken — not as Authorization: Bearer (which Restful Booker rejects with 403)
  • Data-driven scenarios use Scenario Outline with an Examples table for multiple booking payloads; larger datasets are externalised to data/booking-payloads.json or data/booking-data.csv and loaded with read()
  • Negative test data is explicit and versioned in the data/ folder: malformed payloads, missing required fields, invalid ids — each with a documented expected response that reflects actual Restful Booker behaviour (which may return 200 or 500 for bad payloads, not always 4xx)
  • Schema assertions use Karate's match syntax with fuzzy markers (#notnull, #string, #number) for shape validation, plus explicit value assertions where the response value is deterministic (e.g. bookingid from a create we just did)

Reporting

  • Karate generates a self-contained HTML report at target/karate-reports/karate-summary.html after every run — no additional plugin or reporting server required
  • The summary page shows overall pass/fail counts, execution time per feature, and links to per-scenario detail pages with full Gherkin step output
  • Each scenario detail page shows the request/response log for every HTTP call and the result of every match assertion — no separate logging setup needed
  • GitHub Actions uploads the karate-reports directory as a downloadable artifact on every run so reports are available even when the pipeline fails
  • Do not log tokens or passwords in feature files; use karate.mask() or store credentials in karate-config.js via karate.properties so they are not echoed to the report

CI/CD

  • A .github/workflows/karate-tests.yml workflow named 'Karate API Tests' triggers on push and pull_request to main
  • The workflow checks out the repository with actions/checkout@v4
  • Java 17 is set up with actions/setup-java@v4 using the Temurin distribution
  • The ~/.m2 Maven repository is cached with actions/cache@v4 keyed on pom.xml so dependencies are not downloaded on every run
  • Tests run with mvn test -B; karate-config.js maps the CI environment to the Restful Booker base URL and reads qa.username and qa.password from GitHub Actions repository secrets via -Dqa.username and -Dqa.password
  • The target/karate-reports directory is uploaded as a CI artifact with actions/upload-artifact@v4 and retained for 30 days
  • A /ping health check step before the test run (expect 201) warms up the Heroku dyno and reduces cold-start flakiness on the first scenario

Common issues

403 on PUT, PATCH, or DELETE even with a valid token

Cause: Token is sent as Authorization: Bearer instead of as a Cookie header — Restful Booker rejects Bearer authentication on write operations

Fix: Send the token as a Cookie: Given cookie token = authToken or configure Karate's cookie keyword; Authorization: Bearer is not accepted by this API

DELETE assertion fails — test expects 200 or 204

Cause: Restful Booker returns 201 Created on a successful DELETE /booking/{id}, not 200 or 204

Fix: Assert Then status 201 on delete scenarios; this is documented behaviour, not a bug

First scenario in CI is flaky or times out

Cause: Restful Booker runs on a Heroku free-tier dyno that cold-starts after inactivity; the first request takes several seconds to wake it

Fix: Add a /ping step at the start of the workflow (expect 201) to warm up the dyno before the test suite runs; consider a retry policy for the first scenario

Negative test fails — expected 400 but got 200 or 500

Cause: Restful Booker does not always return a clean 4xx for bad payloads; some invalid requests return 200 with unexpected body or 500

Fix: Probe the actual API behaviour with curl or Postman first, then assert what actually happens — not what ideally should happen; document the quirk in a comment

mvn test reports zero tests found and exits 0

Cause: Maven Surefire defaults to the JUnit 4 engine and does not find JUnit 5 tests without the platform provider configured

Fix: Add the maven-surefire-plugin to pom.xml with junit-platform-launcher as a dependency and set <useModulePath>false</useModulePath>

karate-config.js variables are undefined inside feature files

Cause: karate-config.js is not at the classpath root; it must be at src/test/resources/karate-config.js — not in a subdirectory or under src/test/java

Fix: Confirm the file is at src/test/resources/karate-config.js; Maven treats src/test/resources as a test resource root which is the correct classpath location

call read() returns null or throws 'variable not defined'

Cause: The called feature does not define a return variable, or the classpath: prefix is missing from the path passed to read()

Fix: Use the classpath: prefix for absolute paths — call read('classpath:features/reusable/create-token.feature') — and ensure the called feature ends with an explicit def result = {token: '#(token)'}

Parallel execution produces intermittent assertion failures

Cause: Multiple scenarios target the same bookingid simultaneously on the shared Restful Booker database

Fix: Each scenario must create its own booking in setup and clean up in teardown; never hardcode or share a bookingid across scenarios

Best practices

  • Keep Background blocks focused: only put steps that are truly required by every scenario in the feature — shared setup that only some scenarios need belongs in a reusable feature called inline
  • Reusable features should do one thing and return a clear, named result: create-token.feature returns {token}, create-booking.feature returns {bookingid} — keep them short and readable
  • Validate more than the status code: assert response body shape with match, check specific field values where deterministic, and use fuzzy markers for dynamic fields
  • Keep negative test data explicit and commented: state what the payload is, why it is invalid, and what Restful Booker actually returns — do not assume 400 without probing first
  • Use consistent tags: @smoke for the minimal set CI should always run, @regression for the full suite, @wip to skip in-progress scenarios, and resource tags like @booking for targeted runs
  • Use match response contains rather than match response == when you do not own all response fields — Restful Booker may add fields over time
  • Pass credentials via karate.properties and system properties, not hardcoded strings in feature files or karate-config.js; use environment variables or CI secrets for sensitive values
  • Prefer classpath: paths over relative paths in call read() — they are stable regardless of the working directory from which Maven is invoked

Next steps

  • Integrate Allure reporting by adding the Allure Karate adapter to pom.xml and publishing the allure-results directory as a CI artifact for run-history trend charts
  • Extend schema coverage to all Restful Booker endpoints using JSON schema files in src/test/resources/schemas/ for contract-style validation
  • Add performance assertions using Karate's built-in responseTime variable — assert responseTime < 2000 on all GET endpoints to catch latency regressions on the live Heroku API
  • Build a Karate mock server for the Restful Booker API so the suite can run fully offline during local development without depending on Heroku availability
  • Add Pact consumer contract tests alongside the Karate suite to verify the booking schema is stable across API versions
  • Explore Karate's built-in WebDriver support to add a UI smoke scenario that verifies the booking shows up in a frontend after the API creates it — demonstrating Karate's unified API + UI capability