Q37 of 40 · REST Assured

How would you handle backwards-compatible API changes that require gradual test updates?

REST AssuredSeniorrest-assuredversioningbackwards-compatibilityapi-testingstrategy

Short answer

Short answer: Maintain versioned ResponseSpecifications (v1ResSpec, v2ResSpec) and run both against the live API during the migration window. Tag v1 tests with a custom annotation and track them in CI. When v1 traffic drops to zero and the sunset date arrives, delete the tagged tests with a tracked ticket.

Detail

Backwards-compatible changes (adding optional fields, new endpoints, changed defaults) should not require immediate test updates — existing tests should still pass. Breaking changes (removed fields, changed types) require a versioned deprecation strategy.

ResponseSpecification per version:

ResponseSpecification v1ResSpec = new ResponseSpecBuilder()
    .expectBody("id", notNullValue())
    .expectBody("name", notNullValue())
    .build();

ResponseSpecification v2ResSpec = new ResponseSpecBuilder()
    .expectBody("id", notNullValue())
    .expectBody("name", notNullValue())
    .expectBody("fullName", notNullValue())   // v2 addition
    .expectBody("createdAt", notNullValue())
    .build();

Running both suites in CI against the same API endpoint:

  • v1 tests must pass: proves backwards compatibility is maintained
  • v2 tests added as the new contract: proves new features work
  • v1 tests deleted at sunset: tied to a Jira ticket with the deprecation date

Additive-only tests handle most backwards-compatible changes: new assertions for new fields are added to new tests; existing tests are not modified. A test that asserts body("newOptionalField", ...). would fail against v1 — guard it with version detection or put it only in v2 test classes.

// EXAMPLE

// Version-tagged test
@Tag("v1") @Tag("deprecated-2026-Q3")
@Test
void getUserV1_returnsNameAndEmail() {
    given(reqSpec).when().get("/api/v1/users/1")
    .then()
        .spec(v1ResSpec)
        .body("name",  notNullValue())
        .body("email", notNullValue());
    // No fullName assertion — v1 doesn't have it
}

// v2 test — added alongside v1, not a replacement until sunset
@Test
void getUserV2_returnsFullName() {
    given(reqSpec).when().get("/api/v2/users/1")
    .then()
        .spec(v2ResSpec)
        .body("name",     notNullValue())
        .body("fullName", notNullValue())
        .body("createdAt", notNullValue());
}

// CI pipeline — runs both in parallel; v1 must stay green during deprecation window
// mvn test -Dgroups="v1,v2" (TestNG) or -Dtags="v1 | v2" (JUnit 5)

// WHAT INTERVIEWERS LOOK FOR

Versioned specs for each API version, running both concurrently as the compatibility proof, tagging deprecated tests with sunset dates, and additive-only test changes for new fields. This is a senior concern that rarely appears in tutorial content.

// COMMON PITFALL

Modifying existing tests to add v2 assertions while removing v1 assertions — this drops backwards-compatibility coverage during the migration window, potentially letting a v1 regression go undetected.