TL;DR
- Karate = BDD API testing without Java coding (tests in
.featurefiles)- Syntax: Given/When/Then with built-in JSON/XML assertions
- No separate step definitions needed — assertions are built into DSL
- Includes mocking, performance testing, parallel execution
- Runs on JVM but tests written in Gherkin-like syntax
Best for: Teams wanting BDD without programming, rapid API test development Skip if: Need full programmatic control (use REST Assured) Reading time: 14 minutes
Your team wants BDD-style API tests but doesn’t have Java developers. Writing step definitions feels like boilerplate. You need something faster than traditional Cucumber setups.
Karate combines BDD syntax with built-in assertions — no glue code needed. Write tests in feature files, run them immediately. JSON matching, schema validation, data-driven testing all work out of the box.
What is Karate?
Karate is an open-source API testing framework that uses a Gherkin-like DSL. Unlike Cucumber, it doesn’t require step definitions — everything is built into the syntax.
Why Karate:
- No Java code — tests written in
.featurefiles - Built-in assertions — JSON/XML matching without code
- Parallel execution — fast test suite runs
- Mocking — built-in mock server
- Performance — load testing with Gatling integration
- UI testing — browser automation included
Setup
Maven
<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-junit5</artifactId>
<version>1.4.1</version>
<scope>test</scope>
</dependency>
Project Structure
src/test/java/
├── karate-config.js # Global config
├── logback-test.xml # Logging
└── examples/
├── users/
│ ├── users.feature # Test scenarios
│ └── UsersRunner.java # JUnit runner
└── products/
└── products.feature
JUnit 5 Runner
package examples.users;
import com.intuit.karate.junit5.Karate;
class UsersRunner {
@Karate.Test
Karate testUsers() {
return Karate.run("users").relativeTo(getClass());
}
}
Your First Test
Basic GET Request
Feature: User API
Scenario: Get all users
Given url 'https://api.example.com/users'
When method get
Then status 200
And match response == '#array'
And match each response contains { id: '#number', name: '#string' }
POST with JSON Body
Scenario: Create new user
Given url 'https://api.example.com/users'
And request { name: 'John Doe', email: 'john@example.com' }
When method post
Then status 201
And match response contains { id: '#number', name: 'John Doe' }
JSON Assertions
Exact Match
* match response == { id: 1, name: 'John', active: true }
Partial Match
* match response contains { name: 'John' }
Type Matching
# Match types with markers
* match response.id == '#number'
* match response.name == '#string'
* match response.email == '#? _.contains("@")'
* match response.roles == '#array'
* match response.metadata == '#object'
* match response.deleted == '#null'
* match response.optional == '##string' # Optional (can be null)
Array Assertions
* match response == '#[3]' # Exactly 3 items
* match response == '#[]' # Any array
* match each response == { id: '#number' } # Each item matches
* match response contains { id: 1 } # Array contains item
* match response !contains { id: 999 } # Array doesn't contain
Nested JSON
* match response.user.address.city == 'New York'
* match response..name == ['John', 'Jane'] # All 'name' values
Variables and Reuse
Define Variables
Feature: User Management
Background:
* url 'https://api.example.com'
* def authToken = 'Bearer abc123'
Scenario: Get user profile
Given path '/users/me'
And header Authorization = authToken
When method get
Then status 200
Extract from Response
Scenario: Create and fetch user
Given url 'https://api.example.com/users'
And request { name: 'John' }
When method post
Then status 201
* def userId = response.id
Given url 'https://api.example.com/users/' + userId
When method get
Then status 200
And match response.name == 'John'
Call Other Features
# auth.feature
Feature: Authentication
Scenario: Get token
Given url 'https://api.example.com/auth'
And request { username: 'test', password: 'secret' }
When method post
Then status 200
* def token = response.token
# users.feature
Feature: Users
Background:
* def auth = call read('auth.feature')
* def token = auth.token
Scenario: Get protected resource
Given url 'https://api.example.com/profile'
And header Authorization = 'Bearer ' + token
When method get
Then status 200
Data-Driven Testing
Scenario Outline
Feature: User validation
Scenario Outline: Validate user creation
Given url 'https://api.example.com/users'
And request { name: '<name>', email: '<email>' }
When method post
Then status <status>
Examples:
| name | email | status |
| John | john@example.com | 201 |
| '' | test@example.com | 400 |
| Jane | invalid-email | 400 |
External Data Files
# Read from JSON file
* def testData = read('users-data.json')
Scenario Outline: Test with external data
Given url 'https://api.example.com/users'
And request <user>
When method post
Then status 201
Examples:
| user |
| { name: 'John', email: 'j@test.com' } |
| { name: 'Jane', email: 'jane@test.com' }|
Configuration
karate-config.js
function fn() {
var env = karate.env || 'dev';
var config = {
baseUrl: 'https://api.example.com'
};
if (env === 'dev') {
config.baseUrl = 'https://dev-api.example.com';
} else if (env === 'staging') {
config.baseUrl = 'https://staging-api.example.com';
}
// Global headers
karate.configure('headers', { 'Content-Type': 'application/json' });
return config;
}
Using Config in Tests
Feature: Users API
Background:
* url baseUrl + '/users'
Scenario: Get users
When method get
Then status 200
Parallel Execution
Parallel Runner
import com.intuit.karate.Results;
import com.intuit.karate.Runner;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class ParallelRunner {
@Test
void testParallel() {
Results results = Runner.path("classpath:examples")
.parallel(5); // 5 threads
assertEquals(0, results.getFailCount(), results.getErrorMessages());
}
}
Mock Server
Define Mock
Feature: Mock Server
Background:
* def port = 8080
* def mock = karate.start({ mock: 'mock.feature', port: port })
Scenario: Test with mock
Given url 'http://localhost:' + port + '/users'
When method get
Then status 200
mock.feature
Feature: Mock API
Scenario: pathMatches('/users')
* def response = [{ id: 1, name: 'Mock User' }]
Scenario: pathMatches('/users/{id}')
* def userId = pathParams.id
* def response = { id: '#(userId)', name: 'User ' + userId }
CI/CD Integration
GitHub Actions
name: API Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Run Karate tests
run: mvn test -Dkarate.env=staging
- name: Upload reports
uses: actions/upload-artifact@v4
if: always()
with:
name: karate-reports
path: target/karate-reports/
Karate with AI Assistance
AI tools can help write and maintain Karate tests.
What AI does well:
- Generate feature files from API documentation
- Create JSON match expressions
- Convert Postman collections to Karate
- Suggest data-driven test scenarios
What still needs humans:
- Designing test strategy
- Understanding business validation rules
- Complex conditional logic
- Performance test configuration
FAQ
What is Karate?
Karate is a BDD API testing framework that uses Gherkin-like syntax. Unlike Cucumber, you don’t need Java step definitions — assertions are built into the DSL. Write Given/When/Then scenarios in .feature files and run them immediately. It also includes mocking, performance testing, and UI automation capabilities.
Karate vs REST Assured — which is better?
Karate requires no Java programming — tests are written in .feature files using DSL syntax. REST Assured requires Java code for everything. Karate is faster to learn and easier for non-programmers. REST Assured offers more programmatic control and better IDE support. Choose Karate for rapid test development, REST Assured for complex programmatic needs.
Can Karate test GraphQL?
Yes, Karate handles GraphQL as JSON over HTTP. Use the same request/response syntax for queries and mutations. GraphQL variables work through standard JSON handling. The framework doesn’t distinguish GraphQL from REST — it’s all HTTP with JSON bodies.
Does Karate support parallel execution?
Yes, parallel execution is built-in and easy to configure. Run features across multiple threads with a simple Runner configuration. Karate handles thread safety and resource isolation automatically. This makes it ideal for large test suites needing fast CI/CD feedback.
Official Resources
See Also
- API Testing Tutorial - API testing fundamentals
- REST Assured Tutorial - Java API testing
- Postman Tutorial - GUI-based API testing
- Cucumber Tutorial - BDD with step definitions
