Monday, November 27, 2017

Expected Conditions and Interactions

Getting stable and reliable Web test cases is the goal for any automation engineer. Part of providing test reliability is to check for a particular UI state before attempting to interact with the application further. For example checking that a button is visible before trying to click on it.

Seleniums ExpectedConditions are used to make the test case wait until a certain user interface state is reached before continuing. For example:

  • Wait until an Element is Clickable
  • Wait until an Element is has a specific Text value
  • Wait until an Element is Visible
  • etc.

Individual conditions can then also be combined with AND and OR operators to create more complex conditions, for example wait until an element is Visible AND has a specific Text value.

Checking and waiting for a certain application state is a good approach, it provides a checkpoint in the test to know that the test code and the application are in sync with each other.

However in dynamic websites, user interface states can change, and a particular state may only exist for a short period of time before it changes. In these circumstances when a certain state is reached, it is not guaranteed to be the same state when the test's next step is to interact with the application.

For example, lets assume that we want to click on an button. We first use ExpectedConditions to wait for the element to be visible and clickable, once this state is reached we use findElement to obtain the element and call the click method.

public class ClickButtonTest {

private WebDriver webDriver;
private static final By buttonLocator = By.cssSelector("button");

@BeforeClass
public void setupBrowser() {
webDriver = new FirefoxDriver();
webDriver.get("http://the-internet.herokuapp.com/login");
}

@Test
public void clickLoginButton() {
WebDriverWait wait = new WebDriverWait(webDriver, 5);
wait.until(ExpectedConditions.visibilityOfElementLocated(buttonLocator));
WebElement button = webDriver.findElement(buttonLocator);
button.click();
}

@AfterClass
public void closeBrowser() {
if (webDriver != null) webDriver.quit();
}

}


Here the clickLoginButton method looks quite safe, in that we wait for an expected UI state to be reached, and when reached (up to a max of 5 seconds), we continue to find the element and click it.

The problem can be in dynamic websites is that there is a time delay from the ExpectedCondition being satisfied and the click method being called, the application may not be in the same state, leading to an Exception from either findElement or the click method.

To solve this we need to combine the interaction (i.e. click) with the ExpectedCondition so that they are considered one transaction, either both work or both don't work.

This is where we can add an Expected Interaction and combine it with the Expected Condition.
First we need to add our expected interaction of click:


public class ExpectedInteractions {

public static ExpectedCondition<boolean> wasClickedBy(final By locator) {
return new ExpectedCondition<boolean>() {
public Boolean apply(WebDriver webDriver) {
try {
webDriver.findElement(locator).click();
return true;
}
catch (Exception e) {
return false;
}
}
};
}

}


And now we can use this within the test case, by combining the expected condition and the interaction together, as show below.

@Test
public void clickLoginButton() {
WebDriverWait wait = new WebDriverWait(webDriver, 5);
wait.until(ExpectedConditions.and(ExpectedConditions.visibilityOfElementLocated(buttonLocator), ExpectedInteractions.wasClickedBy(buttonLocator)));
}


Hope this article was useful!

Expected Conditions and Interactions

Getting stable and reliable Web test cases is the goal for any automation engineer. Part of providing test reliability is to check for a par...