WebTau Report

You can use WebTau http. , graphql. , browser. , cli. , db. modules without any additional setup, but to include a test into generated report and to get extra console output you need to use @WebTau annotation or enable global extension (see below) Groovy package com.example.tests.junit5 import org.testingisdocumenting.webtau.junit5.WebTau import org.junit.jupiter.api.* import static org.testingisdocumenting.webtau.WebTauGroovyDsl.* @WebTau // annotation to enable rich console output and html reporting @TestMethodOrder(MethodOrderer.OrderAnnotation) // forcing methods execution order @DisplayName("customer CRUD") class CustomerCrudSeparatedGroovyTest { private static def customerPayload = [firstName: 'FN', lastName : 'LN'] private static def changedCustomerPayload = [*: customerPayload, lastName: 'NLN'] private static def id @BeforeAll @DisplayName("create customer") // optional friendly name for reporting purposes static void createCustomer() { id = http.post("/customers", customerPayload) { body.id // using body prefix is required in this case as id conflicts with class field name } id.shouldNot == 0 } @Test @Order(1) @DisplayName("read customer") void read() { http.get("/customers/$id") { body.should == customerPayload } } @Test @Order(2) // order dependence saves from creating customer on every test @DisplayName("update customer") void update() { http.put("/customers/$id", changedCustomerPayload) { body.should == changedCustomerPayload } http.get("/customers/$id") { body.should == changedCustomerPayload } } @Test @Order(3) // but you can still run each method independently @DisplayName("delete customer") void delete() { http.delete("/customers/$id") { header.statusCode.should == 204 } http.get("/customers/$id") { header.statusCode.should == 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") } } Java 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); } }

Extra Console Output

WebTau produces extra console output when you enable reporting either via annotation or globally (see below). scenario create customers (customer query) > executing HTTP POST http://localhost:33587/customers request (application/json): { "firstName": "CQ_FN1", "lastName": "CQ_LN1" } . header.statusCode equals 201 (0ms) response (application/json): { "id": 7, "firstName": "CQ_FN1", "lastName": "CQ_LN1" } . executed HTTP POST http://localhost:33587/customers (10ms) > executing HTTP POST http://localhost:33587/customers request (application/json): { "firstName": "CQ_FN1", "lastName": "CQ_LN2" } . header.statusCode equals 201 (0ms) response (application/json): { "id": 8, "firstName": "CQ_FN1", "lastName": "CQ_LN2" } . executed HTTP POST http://localhost:33587/customers (8ms) > executing HTTP POST http://localhost:33587/customers request (application/json): { "firstName": "CQ_FN2", "lastName": "CQ_LN2" } . header.statusCode equals 201 (0ms) response (application/json): { "id": 9, "firstName": "CQ_FN2", "lastName": "CQ_LN2" } . executed HTTP POST http://localhost:33587/customers (8ms) [.] create customers (customer query) scenario query by last name (customer query) > executing HTTP GET http://localhost:33587/customers/search/last-name?name=CQ_LN2 . body equals *id │ firstName │ lastName 8 │ "CQ_FN1" │ "CQ_LN2" 9 │ "CQ_FN2" │ "CQ_LN2" (1ms) . header.statusCode equals 200 (0ms) response (application/json): [ {"id": __8__, "firstName": __"CQ_FN1"__, "lastName": __"CQ_LN2"__}, {"id": __9__, "firstName": __"CQ_FN2"__, "lastName": __"CQ_LN2"__} ] . executed HTTP GET http://localhost:33587/customers/search/last-name?name=CQ_LN2 (8ms) [.] query by last name (customer query) scenario query by first name (customer query) > executing HTTP GET http://localhost:33587/customers/search/first-name?name=CQ_FN1 . body equals *id │ firstName │ lastName 7 │ "CQ_FN1" │ "CQ_LN1" 8 │ "CQ_FN1" │ "CQ_LN2" (1ms) . header.statusCode equals 200 (0ms) response (application/json): [ {"id": __7__, "firstName": __"CQ_FN1"__, "lastName": __"CQ_LN1"__}, {"id": __8__, "firstName": __"CQ_FN1"__, "lastName": __"CQ_LN2"__} ] . executed HTTP GET http://localhost:33587/customers/search/first-name?name=CQ_FN1 (9ms) [.] query by first name (customer query) scenario clean up (customer query) > executing HTTP DELETE http://localhost:33587/customers/7 . header.statusCode equals 204 (0ms) [no content] . executed HTTP DELETE http://localhost:33587/customers/7 (8ms) > executing HTTP DELETE http://localhost:33587/customers/8 . header.statusCode equals 204 (0ms) [no content] . executed HTTP DELETE http://localhost:33587/customers/8 (9ms) > executing HTTP DELETE http://localhost:33587/customers/9 . header.statusCode equals 204 (0ms) [no content] . executed HTTP DELETE http://localhost:33587/customers/9 (11ms) [.] clean up (customer query) scenario after all tests (Teardown) [.] after all tests (Teardown) Total time: 81ms Total: 5, Passed: 5, Skipped: 0, Failed: 0, Errored: 0 report is generated: /home/runner/work/webtau/webtau/webtau-junit5-examples/webtau-reports/com.example.tests.junit5.CustomerQueryJavaTest.html

Automatic Extension

To enable report for all the tests without annotating each of them, use JUnit5 https://junit.org/junit5/docs/current/user-guide/#extensions-registration-automatic automatic extension registration org.testingisdocumenting.webtau.junit5.WebTauJunitExtension junit.jupiter.extensions.autodetection.enabled=true

BeforeAll/AfterAll Reporting

@BeforeAll and @AfterAll are separate entries in the generated report Groovy package com.example.tests.junit5 import org.testingisdocumenting.webtau.junit5.WebTau import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import static org.testingisdocumenting.webtau.WebTauGroovyDsl.* @WebTau @DisplayName("customer query") class CustomerQueryGroovyTest { private static def id1 // keep track of created ids to assert and cleanup later private static def id2 private static def id3 @BeforeAll @DisplayName("create customers") static void createCustomers() { id1 = createCustomer("CQ_FN1", "CQ_LN1") id2 = createCustomer("CQ_FN1", "CQ_LN2") id3 = createCustomer("CQ_FN2", "CQ_LN2") } @Test @DisplayName("query by first name") void queryByFirstName() { http.get("/customers/search/first-name", [name: "CQ_FN1"]) { body.should == ["*id" | "firstName" | "lastName"] { // star(*) marks key column so assertion is order agnostic __________________________________ id1 | "CQ_FN1" | "CQ_LN1" id2 | "CQ_FN1" | "CQ_LN2" } } } @Test @DisplayName("query by last name") void queryByLastName() { http.get("/customers/search/last-name", [name: "CQ_LN2"]) { body.should == ["*id" | "firstName" | "lastName"] { __________________________________ id2 | "CQ_FN1" | "CQ_LN2" id3 | "CQ_FN2" | "CQ_LN2" } } } @AfterAll @DisplayName("clean up") static void cleanup() { [id1, id2, id3].each { http.delete("/customers/$it") } } private static def createCustomer(String firstName, String lastName) { return http.post("/customers", [firstName: firstName, lastName: lastName]) { id } } } Java package com.example.tests.junit5; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.testingisdocumenting.webtau.junit5.WebTau; import java.util.Map; import java.util.stream.Stream; import static org.testingisdocumenting.webtau.WebTauDsl.*; @WebTau @DisplayName("customer query") class CustomerQueryJavaTest { private static Integer id1; // keep track of created ids to assert and cleanup later private static Integer id2; private static Integer id3; @BeforeAll @DisplayName("create customers") static void createCustomers() { id1 = createCustomer("CQ_FN1", "CQ_LN1"); id2 = createCustomer("CQ_FN1", "CQ_LN2"); id3 = createCustomer("CQ_FN2", "CQ_LN2"); } @Test @DisplayName("query by first name") void queryByFirstName() { http.get("/customers/search/first-name", http.query("name", "CQ_FN1"), (header, body) -> { body.should(equal(table("*id", "firstName", "lastName", // star(*) marks key column so assertion is order agnostic ________________________________, id1, "CQ_FN1" , "CQ_LN1", id2, "CQ_FN1" , "CQ_LN2"))); }); } @Test @DisplayName("query by last name") void queryByLastName() { http.get("/customers/search/last-name", http.query("name", "CQ_LN2"), (header, body) -> { body.should(equal(table("*id", "firstName", "lastName", ________________________________, id2, "CQ_FN1" , "CQ_LN2", id3, "CQ_FN2" , "CQ_LN2"))); }); } @AfterAll @DisplayName("clean up") static void cleanup() { Stream.of(id1, id2, id3).forEach(id -> http.delete("/customers/" + id)); } private static int createCustomer(String firstName, String lastName) { Map<String, Object> payload = map( "firstName", firstName, "lastName", lastName); return http.post("/customers", payload, ((header, body) -> { return body.get("id"); })); } }