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

Karate DSL is a BDD-style API testing framework used by thousands of enterprise teams who need rapid test development without dedicated Java expertise. According to the 2024 SmartBear State of API Testing report, BDD-style API test frameworks have seen 40% year-over-year growth in enterprise adoption. Unlike Cucumber, Karate requires no step definition files — assertions are built into the DSL, enabling testers to write complete API test suites in Gherkin-like syntax without writing a single line of Java. Karate supports REST, SOAP, GraphQL, WebSocket, and even browser automation in a single framework. This guide covers Karate from first feature file to production-grade test suites: JSON validation, data-driven tests, mocking, parallel execution, and CI/CD integration.

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 using Gherkin-like DSL where assertions are built-in, requiring no Java step definitions.

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?

Choose Karate for teams without Java expertise; choose REST Assured for programmatic control and complex test logic.

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 standard JSON over HTTP, supporting queries, mutations, and variables natively.

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 — Karate has built-in parallel execution; configure threads in the JUnit Runner class and Karate handles thread safety automatically.

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

“Karate removes the biggest barrier to BDD adoption: the glue code. When QA engineers can write complete API tests in feature files without programming, test coverage grows dramatically.” — Yuri Kan, Senior QA Lead

See Also