Now we switch to the automation side. We will build a REST Assured project to test JSONPlaceholder. Not a quick script — a proper project with the framework architecture you learned in Chapter 9.
jsonplaceholder-api-tests/
├── pom.xml
├── testng.xml
├── test-plan.md
├── src/
│ └── test/
│ ├── java/
│ │ └── com/capstone/api/
│ │ ├── config/
│ │ │ └── ConfigReader.java
│ │ ├── base/
│ │ │ └── BaseTest.java
│ │ ├── pojo/
│ │ │ ├── CreatePostRequest.java
│ │ │ └── PostResponse.java
│ │ ├── tests/
│ │ │ ├── PostCrudTests.java
│ │ │ ├── PostSchemaTests.java
│ │ │ ├── PostNegativeTests.java
│ │ │ ├── PostDataDrivenTests.java
│ │ │ ├── UserTests.java
│ │ │ └── CommentTests.java
│ │ └── utils/
│ │ └── TestDataReader.java
│ └── resources/
│ ├── config/
│ │ └── dev.properties
│ ├── schemas/
│ │ ├── post-schema.json
│ │ ├── user-schema.json
│ │ └── comment-schema.json
│ └── testdata/
│ └── users.json<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.capstone</groupId>
<artifactId>jsonplaceholder-api-tests</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<rest-assured.version>5.4.0</rest-assured.version>
<testng.version>7.9.0</testng.version>
<jackson.version>2.17.0</jackson.version>
<allure.version>2.25.0</allure.version>
</properties>
<dependencies>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest-assured.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>${rest-assured.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-testng</artifactId>
<version>${allure.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-rest-assured</artifactId>
<version>${allure.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
<systemPropertyVariables>
<env>${env}</env>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>base.url=https://jsonplaceholder.typicode.com
connection.timeout=5000
response.timeout=10000package com.capstone.api.config;
import java.io.InputStream;
import java.util.Properties;
public class ConfigReader {
private static final Properties props = new Properties();
private static boolean loaded = false;
private ConfigReader() {}
public static void loadConfig() {
if (loaded) return;
String env = System.getProperty("env", "dev");
String file = "config/" + env + ".properties";
try (InputStream is = ConfigReader.class.getClassLoader()
.getResourceAsStream(file)) {
if (is == null) throw new RuntimeException("Config not found: " + file);
props.load(is);
loaded = true;
} catch (Exception e) {
throw new RuntimeException("Failed to load: " + file, e);
}
}
public static String getBaseUrl() {
loadConfig();
return props.getProperty("base.url");
}
}package com.capstone.api.base;
import com.capstone.api.config.ConfigReader;
import io.qameta.allure.restassured.AllureRestAssured;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.filter.log.LogDetail;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeSuite;
public abstract class BaseTest {
protected RequestSpecification requestSpec;
@BeforeSuite
public void suiteSetup() {
ConfigReader.loadConfig();
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
}
@BeforeClass
public void baseSetup() {
requestSpec = new RequestSpecBuilder()
.setBaseUri(ConfigReader.getBaseUrl())
.setContentType(ContentType.JSON)
.setAccept(ContentType.JSON)
.addFilter(new AllureRestAssured())
.log(LogDetail.URI)
.log(LogDetail.METHOD)
.log(LogDetail.BODY)
.build();
}
}For this capstone, we skip the endpoint layer. Why? Because the focus is on comprehensive testing, not framework architecture. You already learned endpoint classes in Chapter 9. Here, we write tests directly with REST Assured to keep things focused. In a real project, add the endpoint layer.
Key Point: The project uses the same Maven + TestNG + REST Assured + Allure stack from Chapter 9. ConfigReader loads the base URL. BaseTest sets up the request spec with AllureRestAssured filter. Every test class extends BaseTest.
Key Point: Set up a clean Maven project with REST Assured, TestNG, Jackson, and Allure. ConfigReader handles environment config. BaseTest provides the request spec. Everything under src/test/.