The base layer is where you set up everything that every test needs. Base URI. Content type. Headers. Logging. Auth tokens. If you write this setup in every test class, you will go mad maintaining it. Write it once in BaseTest. Every test class extends it.
package com.company.api.base;
import com.company.api.config.ConfigReader;
import io.qameta.allure.restassured.AllureRestAssured;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.filter.log.LogDetail;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeSuite;
public abstract class BaseTest {
protected RequestSpecification requestSpec;
protected ResponseSpecification responseSpec;
@BeforeSuite
public void suiteSetup() {
// Load config once for the entire suite
ConfigReader.loadConfig();
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
}
@BeforeClass
public void baseSetup() {
requestSpec = new RequestSpecBuilder()
.setBaseUri(ConfigReader.getBaseUrl())
.setContentType(ContentType.JSON)
.setAccept(ContentType.JSON)
.addFilter(new AllureRestAssured()) // attach req/res to Allure report
.log(LogDetail.URI)
.log(LogDetail.METHOD)
.log(LogDetail.BODY)
.build();
responseSpec = new ResponseSpecBuilder()
.log(LogDetail.STATUS)
.log(LogDetail.BODY)
.build();
}
}Every test class extends BaseTest. They get requestSpec and responseSpec for free. No setup code. No duplication.
BaseEndpoint holds the request spec that all endpoint classes need. Instead of passing the spec to every endpoint constructor manually, you set it once and all endpoints inherit it.
package com.company.api.base;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import static io.restassured.RestAssured.given;
public abstract class BaseEndpoint {
protected final RequestSpecification spec;
public BaseEndpoint(RequestSpecification spec) {
this.spec = spec;
}
// Common helper — extract status code
protected int getStatusCode(Response response) {
return response.getStatusCode();
}
// Common helper — extract response as string
protected String getResponseBody(Response response) {
return response.getBody().asString();
}
// Common GET request
protected Response doGet(String path) {
return given()
.spec(spec)
.when()
.get(path);
}
// Common POST request
protected Response doPost(String path, Object body) {
return given()
.spec(spec)
.body(body)
.when()
.post(path);
}
// Common PUT request
protected Response doPut(String path, Object body) {
return given()
.spec(spec)
.body(body)
.when()
.put(path);
}
// Common DELETE request
protected Response doDelete(String path) {
return given()
.spec(spec)
.when()
.delete(path);
}
}Notice that BaseTest is abstract. You cannot instantiate it directly. It exists only to be extended. Same with BaseEndpoint. This is intentional — they provide shared behavior, not standalone functionality.
Key Point: The AllureRestAssured filter in the RequestSpec is the magic line. It automatically captures every HTTP request and response and attaches them to the Allure report. You add it once. Every test gets detailed logging for free.
Q: What is the role of BaseTest in your framework?
A: BaseTest is an abstract class that every test class extends. It has a @BeforeSuite method that loads config and enables REST Assured failure logging. The @BeforeClass method creates a RequestSpecification with base URI from config, content type JSON, Allure filter for reporting, and request logging. It also creates a ResponseSpecification for response logging. Every test class inherits these specs — zero duplication. We also have a BaseEndpoint that provides common HTTP methods (doGet, doPost, doPut, doDelete) so individual endpoint classes do not repeat REST Assured boilerplate.
Key Point: BaseTest creates RequestSpec once — base URI, content type, Allure filter, logging. BaseEndpoint provides reusable doGet/doPost methods. All test and endpoint classes inherit from these.