Skip to main content

Fluent Design: Chaining Actions and Validations

SHAFT Engine provides a fluent API that lets you chain actions and validations in a single, readable statement. Instead of writing one action per line, you can express an entire test step as a flowing sentence.


Why Fluent Design?​

Traditional test code often looks like a sequence of disconnected commands:

TraditionalStyle.java
driver.browser().navigateToURL("https://example.com/register");
driver.element().type(emailInput, "user@example.com");
driver.element().type(passwordInput, "securePass123");
driver.element().type(confirmPasswordInput, "securePass123");
driver.element().click(registerButton);

With SHAFT's fluent API, the same logic reads like a natural sentence:

FluentStyle.java
driver.browser().navigateToURL("https://example.com/register")
.and().element()
.type(emailInput, "user@example.com")
.and().type(passwordInput, "securePass123")
.and().type(confirmPasswordInput, "securePass123")
.and().click(registerButton);

How It Works​

Every SHAFT action method returns the same action object, allowing you to call the next method directly on the result. The .and() method is syntactic sugar that returns this — it exists purely for readability.

MethodReturnsPurpose
driver.element().click(locator)ActionsPerform click, continue chaining
driver.element().type(locator, text)ActionsType text, continue chaining
.and()Same instanceReadability connector
driver.browser().navigateToURL(url)BrowserActionsNavigate, continue chaining

Chaining Actions with Validations​

The real power of fluent design is combining actions and validations in a single chain:

FluentActionsAndValidations.java
driver.browser().navigateToURL("https://example.com/register")
.and().element()
.type(emailInput, validEmail)
.and().type(passwordInput, validPassword)
.and().type(confirmPasswordInput, validPassword)
.and().click(registerButton)
.and().assertThat(confirmationLabel)
.text().contains("Registration successful")
.withCustomReportMessage("Verify registration completed with correct data");
tip

The .withCustomReportMessage() method adds a descriptive label in the Allure report, making it easy to understand what each validation checks.


Switching Between Action Types​

You can switch between browser actions, element actions, and validations within the same chain:

SwitchingActionTypes.java
driver.browser().navigateToURL("https://example.com/login")
.and().element()
.type(usernameInput, "admin")
.and().type(passwordInput, "password")
.and().click(loginButton)
.and().browser()
.navigateToURL("https://example.com/dashboard")
.and().assertThat(welcomeMessage)
.text().contains("Welcome, admin");

Best Practices​

  1. Use .and() for readability — It does nothing functionally, but it makes chains read like English.
  2. Keep chains focused — One chain should represent one logical test step or user action.
  3. Add custom report messages — Use .withCustomReportMessage() on validations so your Allure report tells a clear story.
  4. Indent for clarity — Align chained calls to visually group related actions together.
info

Fluent design is not just syntactic sugar — it encourages you to think about tests as user stories, which leads to more maintainable test suites.