Back to Karate

Karate v1 to v2 Migration Guide

docs/MIGRATION_GUIDE.md

2.0.730.7 KB
Original Source

Karate v1 to v2 Migration Guide

What's New in Karate v2

Karate v2 is a complete ground-up rewrite with significant improvements across the board. Here are the highlights:

Performance & Scalability

ImprovementDescriptionCommit
Embedded JS EngineFast hand-rolled lexer/parser with ES6+ support, focused on the Java interop use-case, see benchmark90d6e07
Virtual ThreadsJava 21+ unlocks massive parallelism with minimal overhead-
@lock TagScenario-level mutual exclusion for parallel safety (@lock=name)a08337b
@lock=*Exclusive execution - scenario runs alonecd94b11

Assertions

ImprovementDescriptionCommit
match withinFrequently requested - assert that a value falls within a range8535be0
karate.faker.*Built-in test data generation: firstName(), email(), randomInt(), etc.245c540
karate.expect()Chai-style BDD assertions - easier migration from Postman!ad2f475
karate.uuid()Generate random UUIDscb516d4

Modern HTTP Client

ImprovementDescriptionCommit
Apache HttpClient 5.6Modern HTTP client with Brotli compression support1a35bcd
Declarative Authconfigure auth for Basic, Bearer, and OAuth2 with automatic token refresh1a06c64

HTTP Mocks

ImprovementDescriptionCommit
Mock Server RewriteNew JS engine and rewritten from scratch for performance - see MOCKS.mdd84c0e4

Performance Testing

ImprovementDescriptionCommit
Gatling 3.14Re-implemented load testing with pure Java architecture32f8b00

Browser Automation

ImprovementDescriptionCommit
CDP Driver RewriteComplete reimplementation using Chrome DevTools Protocol directly68111e5
PooledDriverProviderAutomatic browser pooling for parallel UI automationb140436
Auto-waitAutomatic waiting before element operations reduces flaky tests67e4c2d

Developer Experience

ImprovementDescriptionCommit
Unified Event SystemSingle RunListener API for observing and controlling test execution - see DESIGN.mdf4240a2
JSONL StreamingMemory-efficient karate-results.jsonl format with real-time progressf4240a2
Modern HTML ReportsBootstrap 5.3 with dark mode, interactive tag filtering3b965b6
JUnit 6 IntegrationStreaming dynamic test generation via @TestFactorya794b02

V1 Compatibility

ImprovementDescriptionCommit
Compatibility Shimscom.intuit.karate package delegates to v2fefb91f
Drop-in MigrationMost v1 code works with just dependency update-

More

  • ANSI colors in console, works even outside IDE
  • HTML report with tags filtering
  • Soft assertions
  • JSON validation works in "soft assertion mode by default"
  • Debugging of JavaScript is possible now
  • Large JSON operations such as "contains" will use disk when needed to avoid out-of-memory issues

Configure Auth Details

Centralized authentication configuration supporting multiple auth types:

javascript
// Basic Auth
configure auth = { type: 'basic', username: 'user', password: 'pass' }

// Bearer Token
configure auth = { type: 'bearer', token: 'your-token' }

// OAuth2 Client Credentials
configure auth = { type: 'oauth2', tokenUrl: 'https://auth.example.com/token', clientId: '...', clientSecret: '...' }

// OAuth2 Authorization Code (PKCE)
configure auth = { type: 'oauth2', flow: 'authorization_code', authUrl: '...', tokenUrl: '...', clientId: '...' }
  • Automatic token refresh for OAuth2
  • Auth configuration inherited by called features

karate.expect() - Chai-style Assertions

If you're migrating from Postman or other frameworks using Chai-style JavaScript syntax, karate.expect() provides a familiar API:

javascript
karate.expect(response.name).to.equal('John')
karate.expect(response.age).to.be.above(18)
karate.expect(response).to.have.property('email')
karate.expect(response.tags).to.include('active')
karate.expect(response.status).to.not.equal('deleted')

Overview

Karate v2 includes backward compatibility shims that allow most v1 code to work with minimal changes. For most users, the only change required is updating the Maven dependency.

Quick Start

Step 1: Update Maven Dependencies

xml
<!-- v1 -->
<dependency>
    <groupId>io.karatelabs</groupId>
    <artifactId>karate-junit5</artifactId>
    <version>1.5.2</version>
    <scope>test</scope>
</dependency>

<!-- v2 -->
<dependency>
    <groupId>io.karatelabs</groupId>
    <artifactId>karate-junit6</artifactId>
    <version>2.0.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.1</version>
    <scope>test</scope>
</dependency>

Note: Unlike karate-junit5 which bundled JUnit, karate-junit6 declares JUnit as a provided dependency, giving you control over the JUnit version. You must add junit-jupiter explicitly.

Reference: See the karate-demo migration commit for a complete example of dependency changes.

Step 2: Update Java Version

Karate v2 requires Java 21+ for virtual threads support.

xml
<properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
</properties>

That's it for most projects. Run your tests and they should work.


V1 Compatibility Shims

The following v1 APIs work without code changes via deprecated shims:

v1 ClassStatus
com.intuit.karate.RunnerWorks - delegates to v2
com.intuit.karate.ResultsWorks - wraps v2 SuiteResult
com.intuit.karate.core.MockServerWorks - delegates to v2
com.intuit.karate.junit5.KarateWorks - delegates to v2

Example: Runner API (no changes needed)

java
// This v1 code works in v2 without modification
import com.intuit.karate.Results;
import com.intuit.karate.Runner;

Results results = Runner.path("classpath:features")
    .tags("~@ignore")
    .parallel(5);
assertTrue(results.getFailCount() == 0, results.getErrorMessages());

Example: MockServer API (no changes needed)

java
// This v1 code works in v2 without modification
import com.intuit.karate.core.MockServer;

MockServer server = MockServer
    .feature("classpath:mock.feature")
    .arg("key", "value")
    .http(0).build();

While the v1 shims work, we recommend migrating to the native v2 APIs to avoid deprecation warnings and take advantage of new features.

Runner API

java
// v1 (deprecated shim)
import com.intuit.karate.Results;
import com.intuit.karate.Runner;

Results results = Runner.path("classpath:features")
    .tags("~@ignore")
    .parallel(5);
assertTrue(results.getFailCount() == 0, results.getErrorMessages());

// v2 (native)
import io.karatelabs.core.Runner;
import io.karatelabs.core.SuiteResult;

SuiteResult result = Runner.path("classpath:features")
    .tags("~@ignore")
    .parallel(5);
assertEquals(0, result.getScenarioFailedCount(), String.join("\n", result.getErrors()));

Key differences:

  • ResultsSuiteResult
  • getFailCount()getScenarioFailedCount()
  • getErrorMessages()getErrors() (returns List<String>)

MockServer API

java
// v1 (deprecated shim)
import com.intuit.karate.core.MockServer;

MockServer server = MockServer
    .feature("classpath:mock.feature")
    .arg("key", "value")
    .http(0).build();
int port = server.getPort();

// v2 (native)
import io.karatelabs.core.MockServer;

MockServer server = MockServer
    .feature("classpath:mock.feature")
    .arg(Map.of("key", "value"))
    .pathPrefix("/api")
    .start();
int port = server.getPort();
server.stopAndWait();  // clean shutdown

Key differences:

  • .http(0).build().start() (port 0 is the default for dynamic port)
  • .arg("key", "value").arg(Map.of("key", "value"))
  • .pathPrefix("/api") strips path prefix from incoming requests before matching
  • Use .stopAndWait() or .stopAsync() for cleanup

JUnit Runner (Karate)

java
// v1 (deprecated shim)
import com.intuit.karate.junit5.Karate;

class SampleTest {
    @Karate.Test
    Karate testAll() {
        return Karate.run("sample").relativeTo(getClass());
    }
}

// v2 (native)
import io.karatelabs.junit6.Karate;
import org.junit.jupiter.api.DynamicNode;

class SampleTest {
    @Karate.Test
    Iterable<DynamicNode> testAll() {
        return Karate.run("sample").relativeTo(getClass());
    }
}

Key difference: Return type changes from Karate to Iterable<DynamicNode> for JUnit 6 dynamic test support.

Gatling Performance Testing

java
// v1
import static com.intuit.karate.gatling.javaapi.KarateDsl.*;

// v2
import static io.karatelabs.gatling.KarateDsl.*;

The DSL methods (karateProtocol, karateFeature, karateSet, uri) are the same — only the package changes.

Java 17+ --add-opens requirement. Gatling 4.7+ calls MethodHandles.privateLookupIn on java.lang internals, which the JVM blocks unless java.base/java.lang is opened to unnamed modules. Add this to the gatling-maven-plugin configuration:

xml
<plugin>
  <groupId>io.gatling</groupId>
  <artifactId>gatling-maven-plugin</artifactId>
  <version>4.7.0</version>
  <configuration>
    ...
    <jvmArgs>
      <jvmArg>--add-opens=java.base/java.lang=ALL-UNNAMED</jvmArg>
    </jvmArgs>
  </configuration>
</plugin>

Without it the simulation crashes on startup with IllegalAccessException: module java.base does not open java.lang to unnamed module.

Logging

V2 unifies the v1 logging knobs (logPrettyRequest, logPrettyResponse, printEnabled, lowerCaseResponseHeaders, logModifier) under a single configure logging bucket. The shape:

javascript
configure logging = {
  report:  'debug',         // threshold for report-buffer capture (default DEBUG)
  console: 'info',          // threshold for SLF4J/console output (default INFO)
  pretty:  true,            // pretty-print HTTP JSON bodies (default true)
  mask: {                   // HTTP-only redaction (replaces v1 HttpLogModifier)
    headers:    ['Authorization', 'Cookie'],
    jsonPaths:  ['$.password', '$..token'],
    patterns:   [{ regex: '\\bBearer [A-Za-z0-9._-]+\\b', replacement: 'Bearer ***' }],
    replacement: '***',
    enableForUri: function(uri) { return uri.indexOf('/health') < 0 }
  }
}

See DESIGN.md § Logging for the full shape and semantics.

Where do HTTP bodies show up?

Two channels, two thresholds. The HTML report has full bodies by default — you do not need to set anything to see them.

What you wantreportconsoleNotes
Bodies in HTML report, quiet console (default)'debug''info'One-liner per request on stdout, full headers + body in HTML / JSONL / JUnit / Cucumber.
Bodies on console too'debug''trace'Streams full request + body to stdout. Noisy — use locally for debugging, not CI.
Headers on console (no body), bodies in report'debug''debug'Useful when you want to see what URL/headers fired without dumping bodies on screen.
Quiet report and console'warn''warn'HTTP request lines drop out of HTML too. Last resort for sensitive runs — see also @report=false below.

Why this differs from v1. v1 wired a single Logback level for everything, and emitted full bodies at DEBUG. v2 splits report-buffer threshold from console threshold, and the console is auto-tiered: INFO = one-liner, DEBUG = headers, TRACE = body. If your v1 muscle memory was "set DEBUG to see bodies in the terminal," in v2 just open target/karate-reports/karate-summary.html — the bodies are already there.

V1 → V2 mapping

V1V2 equivalent
configure logPrettyRequest = true/falseconfigure logging = { pretty: true/false } — single boolean for both directions
configure logPrettyResponse = true/falseconfigure logging = { pretty: true/false } — same single boolean
configure printEnabled = falseconfigure logging = { report: 'warn' } — raise threshold to drop INFO print/karate.log lines
configure lowerCaseResponseHeaders = trueDropped. match header X-Foo is already case-insensitive; use karate.lowerCase(responseHeaders) for direct map access
configure logModifier = MyImplconfigure logging = { mask: {...} } — declarative map, no Java class needed
configure report = { logLevel: 'warn' }Hard-removed in 2.0.6 — use configure logging = { report: 'warn' }
Manual LoggerFactory.getLogger('com.intuit.karate').setLevel(...) for mid-test silencing* configure logging = { console: 'error' } — auto-restored at scenario end

The four v1 keys above (logPrettyRequest, logPrettyResponse, printEnabled, lowerCaseResponseHeaders) are silent no-ops with deprecation warnings — your tests still run, you just see a one-line WARN per process pointing at the new shape. logModifier likewise warns; rewrite the masking declaratively as shown above.

CLI flag rename (breaking)

V2 < 2.0.6V2 ≥ 2.0.6
--report-log-level--log-report
--runtime-log-level--log-console

karate-pom.json output.logLevel is replaced by a top-level logging block — using the old key now raises a migration error:

json
{
  "logging": {
    "report": "debug",
    "console": "info"
  }
}

@report=false is back

Tag a scenario @report=false to keep it in the run (counts toward suite totals) but suppress its step detail from HTML / JUnit XML / Cucumber JSON / JSONL outputs. Use this for runs where step content (HTTP bodies, error messages) may include secrets that mustn't reach CI artifacts.

  • Failures still surface — but the error message is redacted to output suppressed by @report=false (full detail in runtime logs).
  • Full failure detail still hits SLF4J / Logback (configurable via --log-console), so local debugging works.
  • Suppression propagates to features called from a @report=false scenario.
gherkin
@report=false
Scenario: warmup with sensitive credentials
  * call read('classpath:auth/login.feature')

Feature File Compatibility

Most feature files work unchanged. Known differences:

  • Cookie domain assertions: If testing cookie domains, note that RFC 6265 compliance means leading dots are stripped (.example.comexample.com).
  • Karate-JSON vs JavaScript on the RHS: as in v1, any def / set / configure / match RHS that starts with { or [ is parsed as Karate's relaxed JSON. To force JavaScript / ES6 semantics on the value side, wrap the literal in parens. See below.

Karate-JSON vs JavaScript on the right-hand side

Anything on the right-hand side of def (or set, configure, match, …) that starts with { or [ goes through Karate's relaxed JSON parser — hyphenated keys work, #(expr) is substituted, and a bare identifier on the value side is read as the string with that name. To get JavaScript / ES6 evaluation instead, wrap the literal in parens.

gherkin
* def id = 123
* def name = 'sample'

# Karate JSON — #(...) substitution preserves types
* def a = { "id": #(id), "name": "sample" }
* def b = { id: '#(id)', name: 'sample' }       # equivalent

# Hyphenated keys are fine
* def headers = { Accept: 'application/json', Content-Type: 'application/json', Idempotency-Key: 'abc-123' }

# Paren-wrap forces JavaScript evaluation
* def c = ({ id, name })                        # ES6 shorthand
* def d = ({ id: id, name: name })              # explicit reference

This matches v1 behavior — most v1 feature files work unchanged. The only thing to watch for is feature/test code that intentionally relied on JS semantics for an unwrapped literal (e.g., * def response = { id: pathParams.id }) — those need paren-wrapping (* def response = ({ id: pathParams.id })) or rewriting with #(...).


Browser Automation (UI Tests)

V2 uses a rewritten driver with CDP (Chrome DevTools Protocol) as the primary backend and full W3C WebDriver support for cross-browser testing.

Driver Configuration

javascript
// karate-config.js - minimal config
function fn() {
  karate.configure('driver', { type: 'chrome', headless: false });
  return { serverUrl: 'http://localhost:8080' };
}

Key differences from v1:

  • showDriverLog has no effect (TODO)
  • W3C WebDriver types are fully supported: chromedriver, geckodriver, safaridriver, msedgedriver
  • submit() has been ported from v1 and works on all backends

Driver Types

TypeBackendDescription
chromeCDPChrome/Chromium/Edge via DevTools Protocol (recommended for development)
chromedriverW3CChrome via chromedriver
geckodriverW3CFirefox via geckodriver
safaridriverW3CSafari (macOS only)
msedgedriverW3CMicrosoft Edge

Gherkin Syntax (unchanged)

All v1 driver keywords work the same way:

gherkin
* driver serverUrl + '/login'
* input('#username', 'admin')
* click('button[type=submit]')
* waitFor('#dashboard')
* match driver.title == 'Welcome'

Driver in Called Features (V1-Compatible)

V2 preserves V1 behavior for shared-scope calls (* call read('feature')): if a called feature creates a driver, it automatically propagates back to the caller. No special configuration is needed.

gherkin
# login.feature — driver propagates to caller automatically
@ignore
Feature: Login

Background:
  * configure driver = { type: 'chrome' }

Scenario: Login
  * driver serverUrl + '/login'
  * input('#username', 'admin')
  * input('#password', 'secret')
  * click('#submit')
  * waitFor('#dashboard')
gherkin
# main.feature — driver is available after call returns
Scenario: Full regression
  * call read('classpath:pages/login.feature')
  * delay(5000)                                    # ✅ works — driver propagated from login
  * call read('classpath:pages/dashboard.feature') # ✅ works — driver is shared

Note: Early v2 releases required scope: 'caller' in the driver config. This is no longer needed and can be safely removed.

Driver-Bound Functions

These functions are only available after driver 'url' has been called (i.e., a browser session is active):

click(), input(), clear(), focus(), scroll(), select(), submit(), text(), html(), value(), attribute(), exists(), enabled(), position(), locate(), locateAll(), waitFor(), waitForText(), waitForUrl(), waitUntil(), screenshot(), highlight(), delay(), script(), scriptAll(), mouse(), keys(), switchFrame(), switchPage(), refresh(), back(), forward()

If you see <function> is not defined, check that the driver was initialized before that line.

For a delay without a driver, use karate.pause(millis) instead of delay(millis).

Browser Pooling (Default Behavior)

V2 automatically pools browser instances using PooledDriverProvider. This is the default — no configuration needed:

java
Runner.path("features/")
    .parallel(4);  // Pool of 4 drivers auto-created

Benefits:

  • Browser instances are reused across scenarios
  • Pool size auto-scales to match parallelism
  • Clean state reset between scenarios

See DRIVER.md for detailed DriverProvider documentation.

Element DOM Navigation

V2 drops v1's tree-walking element accessors — parent, children, firstChild, lastChild, previousSibling, nextSibling — by design. Hop-counting patterns like e.parent.parent are fragile: any intervening <div> added by a designer silently breaks the test. The v2 surface is selector-based and mirrors the native W3C DOM Element API:

APIPurpose
element.closest(selector)Nearest ancestor (or self) matching a CSS selector
element.matches(selector)Boolean: does this element match the selector
element.locate(childSelector) / element.locateAll(childSelector)Scoped descendant lookups
element.script(jsExpression)Escape hatch for arbitrary DOM walks (_.nextElementSibling.id, etc.)

V1 → V2 translation recipes:

gherkin
# v1: row.parent.click() — v2: closest + a selector
* locate('//td[text()="John"]').closest('tr').click()

# v1: el.parent.children → v2: closest + scoped locateAll
* def cells = locate('//td[text()="John"]').closest('tr').locateAll('td')

# v1: el.nextSibling — v2: script() drops into the browser
* def nextId = locate('#anchor').script('_.nextElementSibling.id')

Migration Checklist for Driver Tests

  • Remove scope: 'caller' from driver config if present (no longer needed)
  • Replace delay(millis) with karate.pause(millis) if used before the driver starts
  • showDriverLog has no effect (TODO)
  • W3C WebDriver types (chromedriver, geckodriver, safaridriver) are now fully supported
  • Rewrite v1 tree-walking (element.parent, .children, .nextSibling, etc.) in terms of closest(selector) + scoped locateAll, or element.script() for arbitrary DOM walks

Embedded HTTP Server

The v1 embedded HTTP server (com.intuit.karate.http.HttpServer with ServerConfig and contextFactory) has been replaced with a new architecture in v2:

  • v2 uses io.karatelabs.http.ServerRequestHandler with file-based API routing (/api/todosapi/todos.js)
  • Sub-path routing is supported: /api/todos/{id} resolves to api/todos.js with request.pathMatches() for path parameters
  • v1's contextFactory pattern for manual path routing does not exist in v2
  • v1's useGlobalSession(true) is replaced by explicit session init in JS handlers: session || context.init()

Startup pattern: HttpServer.config(...).build()HttpServer.start(port, handler)

java
// v1
ServerConfig config = new ServerConfig("src/main/java/app").useGlobalSession(true);
config.contextFactory(request -> {
    ServerContext context = new ServerContext(config, request);
    if (context.setApiIfPathStartsWith("/api/")) {
        context.setLockNeeded(true);
    }
    return context;
});
HttpServer server = HttpServer.config(config).http(8080).build();

// v2
ServerConfig config = new ServerConfig("src/main/java/app")
        .sessionStore(new InMemorySessionStore())  // required for context.init() to do anything
        .csrfEnabled(false);                       // on by default; turn off for API-only test backends
// /api/* and /pub/* routing is built in — no contextFactory
ServerRequestHandler handler = new ServerRequestHandler(config, new RootResourceResolver(config.getResourceRoot()));
HttpServer server = HttpServer.start(8080, handler);

Two easy-to-miss side effects of the change:

  • Sessions are disabled unless a SessionStore is configured. config.isSessionEnabled() returns true only when sessionStore(...) has been called. Without it, context.init() is a silent no-op and every JS handler dereferencing session.foo throws cannot read properties of null. InMemorySessionStore is the drop-in for dev and single-instance apps.
  • CSRF is enabled by default in v2. A POST from a test client that hasn't first fetched a page and picked up a CSRF token is rejected. For pure API demos, set csrfEnabled(false); for web apps, use csrfExemptPaths(...) for webhook/API routes that authenticate differently.

If your tests used the embedded HTTP server as a test backend, consider switching to MockServer with a feature file — this is the idiomatic v2 approach for test API backends. See karate-todo for a complete example.

For serving full web applications with templates, see TEMPLATING.md.


Migration Checklist

  • Update karate-junit5karate-junit6 dependency
  • Add junit-jupiter dependency explicitly (v2 doesn't bundle it)
  • Update Java version to 21+
  • Update maven-surefire-plugin to 3.2.5+ (for JUnit 6 support)
  • If using the Gatling plugin, add --add-opens=java.base/java.lang=ALL-UNNAMED to its <jvmArgs>
  • Replace JsonUtils with Json class (if used)
  • Remove code using HttpLogModifier, WebSocketClient, or Driver Java APIs (if used)
  • Update cookie domain assertions if needed
  • If using embedded HTTP server: switch to HttpServer.start(port, ServerRequestHandler), add a SessionStore, and decide whether to keep CSRF on
  • If migrating to native v2 APIs: update imports, return types, and method names (see above)

Gradual Migration to v2 APIs

Each shim provides a toV2*() method if you want to migrate incrementally:

java
// Get underlying v2 Builder
io.karatelabs.core.Runner.Builder v2Builder = v1Builder.toV2Builder();

// Get underlying v2 MockServer
io.karatelabs.core.MockServer v2Server = v1Server.toV2MockServer();

// Get underlying v2 SuiteResult
io.karatelabs.core.SuiteResult v2Results = v1Results.toSuiteResult();

CI/CD with Testcontainers

For a complete reference CI/CD pipeline that runs API tests, UI tests (via a containerized Chrome), Gatling smoke, secret scanning, and publishes HTML reports to GitHub Pages, see karatelabs/karate-todo.

The UI side uses chromedp/headless-shell + Testcontainers.exposeHostPorts(...) to reach an in-process Karate-hosted app from inside the browser container. The critical pattern:

java
// src/test/java/app/ui/UiTest.java
private static final int PORT = 18080;

static {
    // Docker 29.x API negotiation workaround
    System.setProperty("api.version", "1.44");
}

@BeforeAll
static void beforeAll() {
    server = App.start(App.serverConfig("src/main/java/app"), PORT);
    chrome = new ChromeContainer();  // extends GenericContainer
    chrome.start();
}

@Test
void testAll() {
    ContainerDriverProvider provider = new ContainerDriverProvider(chrome);
    SuiteResult result = Runner.path("classpath:app/ui")
            .tags("~@external", "~@todo")
            .systemProperty("serverUrl", chrome.getHostAccessUrl(PORT))
            .systemProperty("apiUrl", "http://localhost:" + PORT)
            .driverProvider(provider)
            .parallel(1);
    assertEquals(0, result.getScenarioFailedCount(), String.join("\n", result.getErrors()));
}

ContainerDriverProvider is a thin extension of PooledDriverProvider that overrides createDriver(config) to call CdpDriver.connect(container.getCdpUrl(), CdpDriverOptions.fromMap(config)) — each pooled slot gets a fresh tab in the shared container. Full source: app/ui/support/.


Getting Help


Appendix: Migration Reference Commits

We've migrated two projects as reference implementations. These commits show all the changes needed:

karate-demo Migration

Commit: c8fca97ce

This involved additional infrastructure changes beyond what typical end-users need:

  • Spring Boot 2.x → 3.x upgrade (required for Java 21)
  • javax.*jakarta.* servlet imports
  • Spring Security 5 → 6 configuration style
  • Cookie domain normalization for RFC 6265 compliance

karate-e2e-tests Migration

Commit: 7ffe47509

This shows a simpler migration focused on test dependencies and runner changes.

karate-todo Migration

Repository: karatelabs/karate-todo

A complete v1 → v2 migration using native v2 APIs (no shims):

  • karate-junit5karate-junit6 with Iterable<DynamicNode> return type
  • Embedded HTTP server → MockServer for test backend
  • ResultsSuiteResult with updated method names
  • com.intuit.karate.gatlingio.karatelabs.gatling imports

Reference diffs:

  • Bulk v1 → v2 migration: commit d901b3e — pom (Java 21, karate 2.0.x, karate-junit6, surefire 3.5.2, Gatling --add-opens), App.java (v2 HttpServer.start + ServerRequestHandler + SessionStore), JS handlers (session || context.init()), templates (absolute /pub paths, CDN trinity), JUnit 6 runners, Gatling package rename.
  • CI/CD + Testcontainers UI harness on top of v2.0.4: see the Add Testcontainers UI runner... and Add GitHub Actions CI... commits on main.