Lazy Resource

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.junit5.WebTau; import org.junit.jupiter.api.*; import java.util.Map; import static org.testingisdocumenting.webtau.WebTauDsl.*; @WebTau @TestMethodOrder(MethodOrderer.OrderAnnotation.class) // forcing methods execution order @DisplayName("customer CRUD") public class CustomerCrudSeparatedJavaTest { private static final Map<String, ?> customerPayload = aMapOf( "firstName", "FN", "lastName", "LN" ); private static final Map<String, ?> changedCustomerPayload = aMapOf( customerPayload, "lastName", "NLN"); private static int id; @BeforeAll @DisplayName("create customer") // optional friendly name for reporting purposes 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") 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") 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") 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 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); } }

Report

As you can see in the report below, each CRUD operation has its own entry. If you follow this pattern, then you can filter tests by create , update , read , delete to streamline investigation.