Answer
API Versioning in Test Automation
APIs evolve over time. Good test automation handles multiple versions cleanly.
Common API Versioning Patterns
| Pattern | Example |
|---|---|
| URL path versioning | /api/v1/users, /api/v2/users |
| Query parameter | /api/users?version=2 |
| Header versioning | Accept-Version: 2 |
| Subdomain | v2.api.example.com/users |
Strategy 1: Version-Based Test Configuration
Java
// config.properties
api.version=v2
base.url=https://api.example.com
// BaseApiTest.java
public class BaseApiTest {
protected static final String VERSION = ConfigManager.get("api.version");
protected static final String BASE_URL = ConfigManager.get("base.url");
protected String endpoint(String path) {
return BASE_URL + "/api/" + VERSION + path;
}
}
// Usage in test
@Test
public void testGetUsers() {
given()
.when()
.get(endpoint("/users")) // resolves to /api/v2/users
.then()
.statusCode(200);
}
Strategy 2: Separate Test Suites Per Version
CODE
tests/
āāā v1/
ā āāā UserApiV1Test.java
ā āāā OrderApiV1Test.java
āāā v2/
āāā UserApiV2Test.java ā New fields in v2
āāā OrderApiV2Test.java
Java
// V1 Test
public class UserApiV1Test {
private static final String BASE = "/api/v1";
@Test
public void testGetUserV1() {
given().when().get(BASE + "/users/1")
.then().statusCode(200)
.body("$", hasKey("name"))
.body("$", not(hasKey("full_name"))); // v1 uses 'name', not 'full_name'
}
}
// V2 Test
public class UserApiV2Test {
private static final String BASE = "/api/v2";
@Test
public void testGetUserV2() {
given().when().get(BASE + "/users/1")
.then().statusCode(200)
.body("$", hasKey("full_name")) // v2 renamed to 'full_name'
.body("$", hasKey("profile_picture")); // v2 added new field
}
}
Strategy 3: Parameterized Version Tests
Java
@DataProvider(name = "apiVersions")
public Object[][] apiVersions() {
return new Object[][] {
{ "v1" },
{ "v2" }
};
}
@Test(dataProvider = "apiVersions")
public void testUsersEndpointAcrossVersions(String version) {
given()
.when()
.get("/api/" + version + "/users")
.then()
.statusCode(200); // Both versions should return 200
}
Strategy 4: Header-Based Versioning
Java
@Test
public void testApiWithVersionHeader() {
// Request v1 response
given()
.header("Accept-Version", "1.0")
.when()
.get("/api/users")
.then()
.statusCode(200)
.header("API-Version", "1.0");
// Request v2 response ā different fields
given()
.header("Accept-Version", "2.0")
.when()
.get("/api/users")
.then()
.statusCode(200)
.header("API-Version", "2.0")
.body("data[0]", hasKey("full_name")); // v2-specific field
}
CI/CD: Testing Multiple Versions in Pipeline
YAML
# GitHub Actions matrix strategy
strategy:
matrix:
api-version: [v1, v2]
steps:
- name: Run API Tests for ${{ matrix.api-version }}
run: mvn test -Dapi.version=${{ matrix.api-version }}
Best Practices
- āKeep v1 smoke tests running alongside v2 tests during deprecation period
- āUse semantic versioning contracts with Pact
- āDocument version differences in test class Javadoc
- āRemove tests for officially deprecated versions after sunset date
