We have an app that exposes create , read , update , and delete operations for customer records. Records are being served under /customers .Here is an example of a CRUD operations test. Groovy package scenarios.rest.springboot import static org.testingisdocumenting.webtau.WebTauGroovyDsl.* scenario("CRUD operations for customer") { def customerPayload = [firstName: "FN", lastName: "LN"] // new customer data def id = http.post("/customers", customerPayload) { return id // return id value from response body } http.get("/customers/${id}") { body.should == customerPayload // only specified properties will be asserted against } def changedLastName = "NLN" http.put("/customers/${id}", [*:customerPayload, lastName: changedLastName]) { lastName.should == changedLastName // specifying body is optional } http.get("/customers/${id}") { firstName.should == "FN" lastName.should == changedLastName } def changedFirstName = "NFN" http.patch("/customers/${id}", [firstName: changedFirstName]) http.get("/customers/${id}") { firstName.should == changedFirstName lastName.should == changedLastName } http.delete("/customers/${id}") { statusCode.should == 204 } http.get("/customers/${id}") { statusCode.should == 404 } } Check HTTP/import-and-dependencies Import And Dependencies for prerequisites. Java package com.example.tests.junit5; import org.junit.jupiter.api.Test; import org.testingisdocumenting.webtau.junit5.WebTau; import static org.testingisdocumenting.webtau.WebTauDsl.*; @WebTau public class CustomerCrudJavaTest { @Test public void crud() { var customerPayload = http.json( // new customer data "firstName", "FN", "lastName", "LN"); int id = http.post("/customers", customerPayload, ((header, body) -> { return body.get("id"); // return id value from response body })); http.get("/customers/" + id, ((header, body) -> { body.should(equal(customerPayload)); // only specified properties will be asserted against })); String changedLastName = "NLN"; var changedCustomerPayload = http.json( "firstName", "FN", "lastName", "NLN"); http.put("/customers/" + id, changedCustomerPayload, ((header, body) -> { body.get("firstName").should(equal("FN")); body.get("lastName").should(equal(changedLastName)); })); http.get("/customers/" + id, ((header, body) -> { body.should(equal(changedCustomerPayload)); })); http.delete("/customers/" + id, ((header, body) -> { header.statusCode.should(equal(204)); })); http.get("/customers/" + id, ((header, body) -> { header.statusCode.should(equal(404)); })); } } Check HTTP/import-and-dependencies Import And Dependencies for prerequisites.
Groovy One of the benefits of separating one CRUD scenario into multiple is to be able to run one test at a time. In order to make each test runnable independently we will use createLazyResource . package scenarios.rest.springboot import static org.testingisdocumenting.webtau.WebTauGroovyDsl.* def customerPayload = [firstName: "FN", lastName: "LN"] def customer = createLazyResource("customer") { // lazy resource to be created on the first access def id = http.post("/customers", customerPayload) { return id } return new Customer(id: id, url: "/customers/${id}") // definition is below } scenario("customer create") { customer.id.should != null // accessing resource for the first time will trigger POST (in this example) } scenario("customer read") { http.get(customer.url) { // convenient re-use of url defined above body.should == customerPayload } } scenario("customer update") { def changedLastName = "NLN" http.put(customer.url, [*:customerPayload, lastName: changedLastName]) { lastName.should == changedLastName } http.get(customer.url) { lastName.should == changedLastName } } scenario("customer delete") { http.delete(customer.url) { statusCode.should == 204 } http.get(customer.url) { statusCode.should == 404 } } package scenarios.rest.springboot class Customer { Number id String url // store url of the created entity } Note: to run one scenario at a time use sscenario (additional s in front). groovy-standalone-runner/selective-run Read more Java One of the benefits of separating one CRUD @Test into multiple is to be able to run one test at a time. In order to make each test runnable independently we will leverage BeforeAll , AfterAll , and TestMethodOrder . package com.example.tests.junit5; import org.testingisdocumenting.webtau.http.request.HttpRequestBody; import org.testingisdocumenting.webtau.junit5.WebTau; import org.junit.jupiter.api.*; import static org.testingisdocumenting.webtau.WebTauDsl.*; @WebTau // annotation to enable rich console output and html reporting @TestMethodOrder(MethodOrderer.OrderAnnotation.class) // forcing methods execution order @DisplayName("customer CRUD") public class CustomerCrudSeparatedJavaTest { private static final HttpRequestBody customerPayload = http.json( "firstName", "FN", "lastName", "LN"); private static final HttpRequestBody changedCustomerPayload = http.json( "firstName", "FN", "lastName", "NLN"); private static int id; @BeforeAll @DisplayName("create customer") // optional friendly name for reporting purposes public static void createCustomer() { id = http.post("/customers", customerPayload, ((header, body) -> { return body.get("id"); })); actual(id).shouldNot(equal(0)); } @Test @Order(1) @DisplayName("read customer") public void read() { http.get("/customers/" + id, ((header, body) -> { body.should(equal(customerPayload)); })); } @Test @Order(2) // order dependence saves from creating customer on every test @DisplayName("update customer") public void update() { http.put("/customers/" + id, changedCustomerPayload, ((header, body) -> { body.should(equal(changedCustomerPayload)); })); http.get("/customers/" + id, ((header, body) -> { body.should(equal(changedCustomerPayload)); })); } @Test @Order(3) // but you can still run each method independently @DisplayName("delete customer") public void delete() { http.delete("/customers/" + id, ((header, body) -> { header.statusCode.should(equal(204)); })); http.get("/customers/" + id, ((header, body) -> { header.statusCode.should(equal(404)); })); id = -1; // marking as deleted to let cleanup step know that no delete is required } @AfterAll public static void cleanup() { // optional (since we create new ids all the time) step to keep your environment clean if (id == -1) { return; } http.delete("/customers/" + id); } }
Now report has separate entries for each CRUD operation. Makes it possible to filter tests by create , update , read , delete to streamline investigation.