This is the meat of your test suite. A full CRUD test class for the /posts resource on JSONPlaceholder. Every HTTP method. Every assertion. No shortcuts.
package com.capstone.api.pojo;
public class CreatePostRequest {
private String title;
private String body;
private int userId;
public CreatePostRequest() {}
public CreatePostRequest(String title, String body, int userId) {
this.title = title;
this.body = body;
this.userId = userId;
}
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getBody() { return body; }
public void setBody(String body) { this.body = body; }
public int getUserId() { return userId; }
public void setUserId(int userId) { this.userId = userId; }
}package com.capstone.api.tests;
import com.capstone.api.base.BaseTest;
import com.capstone.api.pojo.CreatePostRequest;
import io.qameta.allure.Epic;
import io.qameta.allure.Feature;
import io.qameta.allure.Severity;
import io.qameta.allure.SeverityLevel;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;
@Epic("JSONPlaceholder API")
@Feature("Posts CRUD")
public class PostCrudTests extends BaseTest {
// ===== GET Tests =====
@Test(groups = "smoke", priority = 1)
@Severity(SeverityLevel.BLOCKER)
public void testGetAllPostsReturns100Items() {
given().spec(requestSpec)
.when()
.get("/posts")
.then()
.statusCode(200)
.body("size()", equalTo(100))
.body("[0].id", equalTo(1))
.body("[0].userId", notNullValue())
.body("[0].title", notNullValue())
.body("[0].body", notNullValue());
}
@Test(groups = "smoke", priority = 2)
@Severity(SeverityLevel.BLOCKER)
public void testGetSinglePostById() {
given().spec(requestSpec)
.when()
.get("/posts/1")
.then()
.statusCode(200)
.body("id", equalTo(1))
.body("userId", equalTo(1))
.body("title", not(emptyString()))
.body("body", not(emptyString()));
}
@Test(groups = "regression", priority = 3)
@Severity(SeverityLevel.NORMAL)
public void testGetPostsByUserId() {
given().spec(requestSpec)
.queryParam("userId", 1)
.when()
.get("/posts")
.then()
.statusCode(200)
.body("size()", equalTo(10))
.body("userId", everyItem(equalTo(1)));
}
@Test(groups = "regression", priority = 4)
@Severity(SeverityLevel.NORMAL)
public void testGetCommentsForPost() {
given().spec(requestSpec)
.when()
.get("/posts/1/comments")
.then()
.statusCode(200)
.body("size()", equalTo(5))
.body("postId", everyItem(equalTo(1)))
.body("email", everyItem(containsString("@")))
.body("name", everyItem(notNullValue()))
.body("body", everyItem(notNullValue()));
}
// ===== POST Tests =====
@Test(groups = "smoke", priority = 5)
@Severity(SeverityLevel.BLOCKER)
public void testCreatePost() {
CreatePostRequest newPost = new CreatePostRequest(
"Capstone Test Post",
"Created during the final capstone project",
1
);
given().spec(requestSpec)
.body(newPost)
.when()
.post("/posts")
.then()
.statusCode(201)
.body("title", equalTo("Capstone Test Post"))
.body("body", equalTo("Created during the final capstone project"))
.body("userId", equalTo(1))
.body("id", equalTo(101));
}
// ===== PUT Tests =====
@Test(groups = "regression", priority = 6)
@Severity(SeverityLevel.CRITICAL)
public void testUpdatePostWithPut() {
CreatePostRequest updated = new CreatePostRequest(
"Updated Title",
"Updated body content",
1
);
given().spec(requestSpec)
.body(updated)
.when()
.put("/posts/1")
.then()
.statusCode(200)
.body("title", equalTo("Updated Title"))
.body("body", equalTo("Updated body content"))
.body("userId", equalTo(1))
.body("id", equalTo(1));
}
// ===== PATCH Tests =====
@Test(groups = "regression", priority = 7)
@Severity(SeverityLevel.NORMAL)
public void testPartialUpdateWithPatch() {
given().spec(requestSpec)
.body("{\"title\": \"Patched Title Only\"}")
.when()
.patch("/posts/1")
.then()
.statusCode(200)
.body("title", equalTo("Patched Title Only"))
.body("userId", equalTo(1)) // unchanged field preserved
.body("body", notNullValue()); // unchanged field preserved
}
// ===== DELETE Tests =====
@Test(groups = "smoke", priority = 8)
@Severity(SeverityLevel.CRITICAL)
public void testDeletePost() {
given().spec(requestSpec)
.when()
.delete("/posts/1")
.then()
.statusCode(200);
}
// ===== Filtering Tests =====
@Test(groups = "regression", priority = 9)
@Severity(SeverityLevel.NORMAL)
public void testFilterPostsByUserId() {
given().spec(requestSpec)
.queryParam("userId", 5)
.when()
.get("/posts")
.then()
.statusCode(200)
.body("size()", greaterThan(0))
.body("userId", everyItem(equalTo(5)));
}
@Test(groups = "regression", priority = 10)
@Severity(SeverityLevel.NORMAL)
public void testGetLastPost() {
given().spec(requestSpec)
.when()
.get("/posts/100")
.then()
.statusCode(200)
.body("id", equalTo(100))
.body("userId", equalTo(10));
}
}Ten tests. Four HTTP methods. Filtering. Nested resources. Smoke and regression groups. Allure annotations. This is what a real CRUD test class looks like.
Notice the priority attribute on each test. REST Assured tests should be independent, but priority helps you see results in a logical order — GETs first, then POST, PUT, PATCH, DELETE. It does not create dependencies.
JSONPlaceholder returns 200 for DELETE, not 204. Every API has its own conventions. Always check the actual API behavior before writing assertions. Do not assume standard REST conventions.
Q: Walk me through the CRUD tests you wrote for your capstone project.
A: I tested the /posts resource on JSONPlaceholder with 10 tests across all HTTP methods. GET tests verify the full list returns 100 posts, single post returns correct fields, filtering by userId works, and nested /posts/1/comments returns 5 comments. POST test creates a new post with a POJO body and verifies 201 status, returned data matches input, and an ID is generated. PUT test sends a full update and verifies all fields change. PATCH test sends a partial update and verifies only the specified field changes while others are preserved. DELETE test verifies 200 status. I also added filtering tests for query parameters. Tests are grouped into smoke and regression using TestNG groups.
Key Point: A complete CRUD test class covers GET (all, single, filtered, nested), POST (create with POJO), PUT (full update), PATCH (partial update), and DELETE. Use Hamcrest matchers for fluent assertions.