TL;DR

  • Karate = BDD API testing without Java coding (tests in .feature files)
  • 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 .feature files
  • 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