WebTau

WebTau (Web Test automation) - concise and expressive way to write end-to-end and unit tests.Test your application across multiple layers and use unique features: #rest-api REST API #websocket WebSocket #graphql-api GraphQL API #persona Authorization Personas #browser Browser #fake-static-and-proxy-servers Fake, Static And Proxy Servers #database Database #cli CLI #business-logic-jvm Business Logic (JVM only) #repl REPL #reporting Reporting #documentation-assistance Documentation Assistance There are many modules, but you can use any module you need independently, or use all the modules at once with convenient single imports.

Scripting And JUnit

Tests can be written and groovy-standalone-runner/introduction executed as scripts via command line or using junit5/getting-started JUnit integration and build systems. Groovy scenario("search by specific query") { search.submit("search this") search.numberOfResults.waitToBe > 1 } webtau testscript.groovy Java public class WebSearchJavaTest { @Test public void searchByQuery() { search.submit("search this"); search.numberOfResults.waitToBe(greaterThan(1)); } }

Rest API

WebTau http module lets you exercise and validate HTTP endpoints. It provides a simplified way to make HTTP calls and validate responsesNow with HTTP/data-coverage Data Coverage. Groovy private final def livePrice = http.resource("/prices/:ticker").price ... livePrice.of("IBM").waitToBe > 115 Java private final HttpLazyResponseValue livePrice = http.resource("/prices/:ticker").get("price"); ... livePrice.of("IBM").waitToBe(greaterThan(115))); > waiting for value of /prices/IBM: price to be greater than 115 > [1/3] executing HTTP GET http://localhost:43675/prices/IBM . header.statusCode equals 200 (0ms) response (application/json): { "price": **100** } . [1/3] executed HTTP GET http://localhost:43675/prices/IBM (3ms) > [3/3] executing HTTP GET http://localhost:43675/prices/IBM . header.statusCode equals 200 (0ms) response (application/json): { "price": ~~120~~ } . [3/3] executed HTTP GET http://localhost:43675/prices/IBM (2ms) . value of /prices/IBM: price greater than 115 (210ms) Groovy package scenarios.rest import static org.testingisdocumenting.webtau.WebTauGroovyDsl.* scenario("check weather") { http.get("/weather") { temperature.shouldBe < 100 } } package com.example.tests.junit5 import org.junit.jupiter.api.Test import org.testingisdocumenting.webtau.junit5.WebTau import static org.testingisdocumenting.webtau.WebTauGroovyDsl.* @WebTau class WeatherGroovyTest { @Test void checkWeather() { http.get("/weather") { temperature.shouldBe < 100 } } } 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 WeatherJavaTest { @Test public void checkWeather() { http.get("/weather", (header, body) -> { body.get("temperature").shouldBe(lessThan(100)); }); } } > executing HTTP GET http://localhost:44451/weather . body.temperature less than 100 (0ms) . header.statusCode equals 200 (0ms) response (application/json): { "temperature": ~~88~~ } . executed HTTP GET http://localhost:44451/weather (32ms) HTTP/CRUD-example Read More

WebSocket

WebTau websocket module lets your send, receive and validate websocket messages in a convenient synchronous manner. Groovy def wsSession = websocket.connect("/prices") wsSession.send([symbol: "IBM"]) wsSession.received.waitTo == [ price: greaterThan(100), symbol: "IBM"] wsSession.close() Java var wsSession = websocket.connect("/prices"); wsSession.send(map("symbol", "IBM")); wsSession.received.waitTo(equal(map( "price", greaterThan(100), "symbol", "IBM"))); wsSession.close(); > connecting to websocket /prices . connected to websocket ws://localhost:33587/prices (6ms) > sending text message to ws://localhost:33587/prices {"symbol": "IBM"} . sent text message to ws://localhost:33587/prices (0ms) > waiting for received from ws://localhost:33587/prices to equal {"price": <greater than 100>, "symbol": "IBM"} > [1/25] polling websocket message {"symbol": "IBM", "price": 77} . [1/25] polled new message (1ms) > [25/25] polling websocket message {"symbol": "IBM", "price": 101} . [25/25] polled new message (1ms) . received from ws://localhost:33587/prices equals {"price": <greater than 100>, "symbol": "IBM"} (206ms) > closing websocket ws://localhost:33587/prices . closed websocket ws://localhost:33587/prices (0ms) web-socket/received-messages Read More

GraphQL API

WebTau graphql module lets you exercise and validate a GraphQL API. It provides a simplified way to access the JSON response of an end-point and provides a DSL to execute queries and mutations. Groovy package scenarios.rest import static org.testingisdocumenting.webtau.WebTauGroovyDsl.* scenario("check weather") { def query = "{ weather { temperature } }"; graphql.execute(query) { weather.temperature.shouldBe < 100 } } package com.example.tests.junit4 import org.junit.Test import org.junit.runner.RunWith import org.testingisdocumenting.webtau.junit4.WebTauRunner import static org.testingisdocumenting.webtau.WebTauDsl.graphql @RunWith(WebTauRunner.class) class GraphQLWeatherGroovyIT { @Test void checkWeather() { def query = "{ weather { temperature } }"; graphql.execute(query) { weather.temperature.shouldBe < 100 } } } Java package com.example.tests.junit4; import org.junit.Test; import org.junit.runner.RunWith; import org.testingisdocumenting.webtau.junit4.WebTauRunner; import static org.testingisdocumenting.webtau.WebTauDsl.*; @RunWith(WebTauRunner.class) public class GraphQLWeatherJavaIT { @Test public void checkWeather() { String query = "{ weather { temperature } }"; graphql.execute(query, (header, body) -> { body.get("data.weather.temperature").shouldBe(lessThan(100)); }); } } GraphQL/queries-and-mutations Read More

Persona

Use persona/introduction Persona concept to test API Authorization and collaboration Web Apps like chats and editors. Groovy scenario("my bank balance") { Alice { http.get("/statement") { balance.shouldBe > 100 } } Bob { http.get("/statement") { balance.shouldBe < 50 } } } Java @WebTau public class PersonaHttpJavaTest { @Test public void checkBalance() { Alice.execute(() -> http.get("/statement", (header, body) -> { body.get("balance").shouldBe(greaterThan(100)); })); Bob.execute(() -> http.get("/statement", (header, body) -> { body.get("balance").shouldBe(lessThan(50)); })); } }

Browser

WebTau browser module lets you interact with a browser.High level abstractions streamline location, assertion and async logic.WebTau leverages https://www.selenium.dev Selenium WebDriver to do the heavy lifting. Groovy package scenarios.ui import static org.testingisdocumenting.webtau.WebTauGroovyDsl.* import static pages.Pages.* scenario("search by specific query") { search.submit("search this") search.numberOfResults.waitToBe > 1 } package pages import static org.testingisdocumenting.webtau.WebTauDsl.* class SearchPage { def header = $("#header") def welcomeMessage = $("#welcome") def searchMessage = $("#message") def box = $("#search-box") def resultsArea = $("#results") def results = $("#results .result") def numberOfResults = results.count def submit(query) { browser.open("/search") box.setValue(query) box.sendKeys(browser.keys.enter) } } package pages class Pages { static final def search = new SearchPage() } Java package com.example.tests.junit5; import org.junit.jupiter.api.Test; import org.testingisdocumenting.webtau.junit5.WebTau; import static com.example.tests.junit5.pages.Pages.*; import static org.testingisdocumenting.webtau.WebTauDsl.*; @WebTau public class WebSearchJavaTest { @Test public void searchByQuery() { search.submit("search this"); search.numberOfResults.waitToBe(greaterThan(1)); } } package com.example.tests.junit5.pages; import org.testingisdocumenting.webtau.browser.page.PageElement; import org.testingisdocumenting.webtau.browser.page.PageElementValue; import static org.testingisdocumenting.webtau.WebTauDsl.*; public class SearchPage { private final PageElement box = $("#search-box"); private final PageElement results = $("#results .result"); public final PageElementValue<Integer> numberOfResults = results.count; public void submit(String query) { browser.open("/search"); box.setValue(query); box.sendKeys(browser.keys.enter); } } package com.example.tests.junit5.pages; public class Pages { public static SearchPage search = new SearchPage(); } > initializing webdriver for chrome . initialized webdriver for chrome (476ms) > opening http://localhost:44451/search . opened http://localhost:44451/search (49ms) > setting value search this to by css #search-box > clearing by css #search-box . cleared by css #search-box (39ms) > sending keys search this to by css #search-box . sent keys search this to by css #search-box (56ms) . set value search this to by css #search-box (120ms) > sending keys <enter> to by css #search-box . sent keys <enter> to by css #search-box (28ms) > waiting for count of by css #results .result to be greater than 1 . count of by css #results .result greater than 1 (11ms) browser/basic-configuration Read More

Fake, Static And Proxy Servers

WebTau server module lets you create and control static, fake and proxy servers:Static servers to quickly host HTML, JSON, and similar content Fake servers to control response based request Proxy servers to simulate outages and record interactions for failures investigation def myServer = server.serve("my-server", "data/staticcontent") def router = server.router() .get("/hello/:name") { request -> server.response([message: "hello ${request.param("name")}"]) } .get("/bye/:name") { request -> server.response([message: "bye ${request.param("name")}"]) } def proxyServer = server.proxy("test-proxy-server", targetServer.baseUrl) You can apply overrides to any created server. You can also put servers into a "bad" state. proxyServer.markUnresponsive() def router = server.router() .get("/hello/:name") {request -> server.response([message: "hello ${request.param("name")}"]) } myServer.addOverride(router) servers/introduction Read More

Database

WebTau db module streamlines databases data setup, assertion and waiting on. database/introduction Read More def PRICES = db.table("PRICES") PRICES << [ "id" | "description" | "available" | "type" | "price" ] { _____________________________________________________________________________________________ cell.guid | "nice set" | true | "card" | 1000 // cell.guid generates random guid that can be used for ids cell.guid | "nice set" | true | "card" | cell.above + 10 // cell.above refers values above and can be modified with simple math operations cell.guid | "another set" | permute(true, false) | permute("rts", "fps") | cell.above + 20 } // permute generates additional rows generating new rows with all the permutations

CLI

WebTau cli module helps with running and testing command line tools cli/introduction Read More cli.run('echo hello world') { output.should contain('hello') output.should contain('world') }

Business Logic (JVM)

Powerful WebTau matchers help with complex data validation and provide rich output to help with failure investigation: matchers/introduction Read More Groovy List<Account> accounts = fetchAccounts() TableData expected = ["*id" | "name" | "address"] { // id is a key column _________________________________________ "ac2" | "Works" | [zipCode: "zip2"] // when key is present, comparison is order agnostic "ac1" | "Home" | [zipCode: "zip1"] "ac3" | "My Account" | [zipCode: "zip8"] } accounts.should == expected Java List<Account> accounts = fetchAccounts(); TableData expected = table("*id", "name", "address", // id is a key column ________________________________________, "ac2", "Works", map("zipCode", "zip2"), // when key is present, comparison is order agnostic "ac1", "Home", map("zipCode", "zip1"), "ac3", "My Account", map("zipCode", "zip8")); actual(accounts).should(equal(expected)); X failed expecting [value] to equal *id │ name │ address "ac2" │ "Works" │ {"zipCode": "zip2"} "ac1" │ "Home" │ {"zipCode": "zip1"} "ac3" │ "My Account" │ {"zipCode": "zip8"}: [value][2].address.zipCode: actual: "zip3" <java.lang.String> expected: "zip8" <java.lang.String> ^ [value][1].name: actual: "Work" <java.lang.String> expected: "Works" <java.lang.String> ^ (2ms) address │ description │ id │ name {"city": "TC1", "zipCode": "zip1"} │ "test account" │ "ac1" │ "Home" {"city": "TC2", "zipCode": "zip2"} │ "test account" │ "ac2" │ **"Work"** {"city": "TC3", "zipCode": **"zip3"**} │ "test account" │ "ac3" │ "My Account"

REPL

Use powerful REPL/experiments REPL mode to significantly speed up end-to-end tests development. Build your tests one step at a time without losing time on restarts. webtau:000> $("p") found single element using by css p innerText: hello web page <p>hello web page</p>

Reporting

Leverage out of the box report/introduction rich reporting. Report captures everything you do. Single self-sufficient file that can be slacked or emailed. Permalinks let you share the exact failure problem with your colleagues.

Documentation Assistance

WebTau helps you to capture test artifacts like HTTP/documentation-artifacts API Responses, browser/documentation-artifacts screenshots, command line output to automate your user facing documentation creation.Below is the example of API documentation with example of requests/response captured by a test: Request Responses 200 201