Q35 of 40 · Karate
How do you handle backwards compatibility testing with Karate (e.g. v1 vs v2 API)?
Short answer
Short answer: Organise v1 and v2 tests in separate folders, set baseUrlV1 and baseUrlV2 in karate-config.js. Run both suites in CI against the live API. Tag v1 scenarios with @v1-deprecated and exclude them gradually as client migration completes. The v1 suite acts as a regression guard during the deprecation window.
Detail
Backwards compatibility testing means the v1 API continues to work correctly while v2 is being developed.
Folder structure:
features/
users/
v1/
get-user.feature @tag: v1
create-user.feature @tag: v1
v2/
get-user.feature @tag: v2
create-user.feature @tag: v2
karate-config.js routes per version:
config.userServiceV1 = 'https://api.example.com/v1';
config.userServiceV2 = 'https://api.example.com/v2';
CI strategy:
- Every PR: run
@v2tests (new contract, fast feedback) - Every PR: run
@v1tests (backwards compat guard) - v1 tests must stay green until the v1 sunset date
Deprecation tracking: tag v1 tests with @v1-deprecated-2026-Q2 — the date is visible in the feature file and in reports. Link to a Jira ticket in the feature description.
Additive changes (new optional field): add assertions only in v2 tests; v1 tests don't assert on the new field (they use match contains rather than match == if the field may or may not be present).
Breaking changes (field removal, type change): v1 tests fail → this is intentional — it tells you the v1 contract is broken and clients must migrate before the change ships.
// EXAMPLE
v1/get-user.feature
@v1 @v1-deprecated-2026-Q2
Feature: Get User — v1 contract (maintained until 2026 Q2)
Background:
* url userServiceV1
* header Authorization = 'Bearer ' + bearerToken
# v1 contract: response has {id, name, email} — no fullName, no createdAt
Scenario: Get user returns v1 contract
Given path '/users/1'
When method GET
Then status 200
# Exact match — any extra field added to v1 response will fail this test
# (catching unintentional v1 contract changes)
And match response == {
id: '#number',
name: '#string',
email: '#string'
}
# features/users/v2/get-user.feature
# @v2
# * match response == {
# id: '#number',
# name: '#string',
# fullName: '#string', ← new in v2
# email: '#string',
# createdAt: '#string' ← new in v2
# }// WHAT INTERVIEWERS LOOK FOR
// COMMON PITFALL
// Related questions