Your E2E case: create a record, then verify it appears in the list, then clean it up.
Real API testing chains calls the same way: POST to create, extract the new id from the response, GET with that id to verify.
Single-call tests check endpoints; chained tests check the system: does what I created actually exist? The key skill is extracting a value from one response to use in the next request.
import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.*; import static org.hamcrest.Matchers.*; public class CreatePostTest { @Test void createThenInspect() { String newPost = """ { "title": "Found by automation", "body": "assert_ was here", "userId": 7 } """; // Java text block — handy for JSON int newId = given() .baseUri("https://jsonplaceholder.typicode.com") .header("Content-Type", "application/json") .body(newPost) .when() .post("/posts") .then() .statusCode(201) // 201 = Created .body("title", equalTo("Found by automation")) .extract().path("id"); // pull the id out System.out.println("Server assigned id: " + newId); } }
[INFO] Running CreatePostTest Server assigned id: 101 [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] BUILD SUCCESS
extract().path("id") is the hinge: the response's id becomes a Java variable, ready for the next call — get("/posts/" + newId). That variable-passing between calls is 90% of "advanced" API automation.
.when().put("/posts/1") // full replace — expect 200 .when().patch("/posts/1") // partial update — expect 200 .when().delete("/posts/1") // remove — expect 200/204
Fix: The JSON path doesn't exist in the response — field names are case-sensitive (userId, not userid). Print the whole body with .log().all() and read the real structure.
Fix: path() returns typed values: JSON numbers come back as Integer. Receive it as int id = …, or use extract().jsonPath().getString("id") when you truly want text.
One test class, four tests against /posts:
{"title":"patched"}, assert 200 and the new title in the response.