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.
Repository
View repository ↗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.js— Environment-aware config: baseUrl, username, password per environmentsrc/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-drivensrc/test/resources/features/reusable/— Shared flows called by other features: create-token.feature, create-booking.feature, delete-booking.featuresrc/test/resources/data/— JSON payloads and CSV files used by Scenario Outline data-driven testssrc/test/resources/schemas/— JSON schema files for match-based response shape validationsrc/test/java/codes/qa/karate/runners/— KarateTestRunner.java (sequential) and ParallelTestRunner.java (parallel execution)target/karate-reports/— Karate HTML report generated automatically after each runPrerequisites
- ✓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
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 guidanceSetup & run
Installation
- 1.
Clone the repository once published: git clone <repo-url> && cd karate-api-automation - 2.
Verify tooling: java -version (expect 17+) and mvn -version (expect 3.8+) - 3.
Download dependencies: mvn dependency:resolve - 4.
Compile runner classes: mvn test-compile - 5.
Run a smoke check: mvn test -Dkarate.options="--tags @smoke" - 6.
Open the report: target/karate-reports/karate-summary.html in a browser
Commands
Run full suite
mvn testRuns all .feature files via KarateTestRunner and generates the HTML report in target/karate-reports/
Run against a named environment
mvn test -Dkarate.env=stagingSets 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=ParallelTestRunnerExecutes all features across parallel threads; each scenario must own independent test data
Environment
| Variable | Description | Example | Required |
|---|---|---|---|
karate.env | Selects 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. | staging | No |
qa.baseUrl | Overrides 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.com | No |
qa.username | Restful 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. | admin | No |
qa.password | Restful 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. | password123 | No |
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