Nexial Automation

Test Automation Platform for everyone!

X

Wait Strategies for Web Automation

Description

Web applications are getting fancier by the minute; web designers and developers everywhere seem to be constantly outdoing each other on pushing the boundaries of web development and breaking the conventions of the Internet. Of course, that translates to more work and more challenges for the automation folks…

To achieve effective web automation, different strategies should be considered for different web applications (or frameworks). This article will list out a few of such strategies, with a focus on the “wait”-related techniques. While not an exhaustive list, some of these strategies should be a good starting point towards effective web automation.

Since Nexial uses Selenium and WebDriver behind the scene, many of these “wait” strategies come directly from Selenium. Note that, while Selenium provides various time units, Nexial uses milliseconds throughout for consistency’s sake.

Fixed Wait

Perhaps the most almost direct form of wait, the fixed wait is easy to understand and to implement. One simply adds a “wait” step, allowing the target web application to “catch up”, and then proceed to the intended automation step. For example,

description cmd type command param 1
click to submit login request web click(locator) css=.loginSubmit
wait for page to load base waitFor(waitMs) 5000
click on My Orders link web clickByLabel(label) My Orders

The second step (above) instructs Nexial to wait for 5 seconds (5000 milliseconds), during which the target website (AUT) would complete the login process and load the subsequent page. 5 seconds later, the third step proceeds to click on the “My Orders” link - presumably, such link is rendered and available for interaction at such time.

The commands one can use to implement fixed wait are:

The fixed wait strategy is simple to implement. But it is not without shortfalls:

  • This consistency of the fixed “wait” time has a double-edged effect. While it is predictable, it also means that there is no drift in timing. If there should be any slowness - whether it is due to network latency, or website, or the test harness - the wait time remains the same. This could lead to flaky tests and unnecessary rework. Similarly, if there should be any improvement in response time or website performance, the fixed wait strategy provides no opportunity to cut down the total execution time.
  • The fixed wait strategy also adds an extra step to the execution, thus unnecessarily inflating the total number of execution steps.

It is generally discouraged to use this strategy. One good reason to use fixed wait, however, is when a fixed delay interval is desired. For example, if we want to forcefully refresh the current web page every 2 seconds 10 times:

description cmd type command param 1 param 2
  base save(var,value) try 0
LOOP START: base repeatUntil(steps,maxWaitMs) 4 -1
  try for 10 times number assertGreaterOrEqual(num1,num2) ${try} 10
  refresh current web page web refresh()    
  try again in 2 second web wait(waitMs) 2000  
LOOP END: increment “try” number increment(var,amount) try 1

… or suppose we want to click on the “Check Status” link 10 times with some randomized wait time:

description cmd type command param 1 param 2
  base save(var,value) try 0
LOOP START: base repeatUntil(steps,maxWaitMs) 4 -1
  try for 10 times number assertGreaterOrEqual(num1,num2) ${try} 10
  click “Check Status” web click(locator) //*[string()='Check Status']  
  wait and try again web wait(waitMs) $(random|numeric|2000|8000)  
LOOP END: increment “try” number increment(var,amount) try 1

Page Load Timeout

When an URL is requested to load on the target browser, there is an implicit timeout value to determine if such action should deem complete or not. This timeout value is known as the “Page Load Timeout”, or, in Selenium-speak, pageLoadTimeout. If the requested URL is not loaded after this timeout value, the corresponding step is considered as FAIL. In Nexial, this value is represented via the System variable nexial.web.pageLoadWaitMs. By default, its value is 15000 (milliseconds), or 15 seconds. This System variable has a direct impact on the following commands:

Note:

  • Setting this System value to a negative value will effectively result in an infinite time out (meaning: never time out).
  • For simple or older websites, this System variable generally provides effective control on the page load timeout. But for modern websites or web applications with a good amount of AJAX interaction or JavaScript DOM manipulation, one should consider other techniques (below) to ensure the timely availability of the page elements.
  • Page load timeout is not applicable when the user clicks a link to open a page.

Implicit Wait

When locating a web element, Nexial allows for a certain amount of time before considering such element as unavailable or absent. This in fact is the behavior of the underlying Selenium as used internally by Nexial. This wait, or time tolerance, is known as the implicit wait.

Nexial represents this wait via its System variable nexial.pollWaitMs. The default value of this System variable is 30000 milliseconds (or 30 seconds). When attempting to locate a web element, Nexial polls the corresponding web page until the target element is located or the nexial.pollWaitMs is reached.

Ideally one would predefine the value of nexial.pollWaitMs in either the project.properties file or the data file. Nexial will such for such System variable and set up the appropriate WebDriver with the specified implicit wait time. It is important to note that while it is possible to change the value of this System variable to accommodate slower or unpredictable websites, increasing the value of this System variable will likely impact the overall performance of one’s automation. This is especially true when slower locator strategies like XPath are in use.

Example of Nexial’s web commands affected by the implicit wait (via nexial.pollWaitMs) are (not complete list):

types web commands
assert…  assertAttribute(locator,attrName,value)  assertCssNotPresent(locator,property)  assertCssPresent(locator,property,value)  assertElementByText(locator,text)  assertElementDisabled(locator)  assertElementEnabled(locator)  assertElementPresent(locator)  assertNotVisible(locator)  assertVisible(locator)
save…  saveAttributeList(var,locator,attrName)  saveAttribute(var,locator,attrName)  saveElement(var,locator)  saveElements(var,locator)  saveValues(var,locator)
interact  clickAll(locator)  clickByLabel(label)  clickOffset(locator,x,y)  deselect(locator,text)  dragAndDrop(fromLocator,toLocator)  selectMultiByValue(locator,array)  selectMulti(locator,array)  select(locator,text)  toggleSelections(locator)  type(locator,value)  upload(fieldLocator,file)

Note

Note that when the specified wait time has expired but the target element is not present, Nexial will consider the corresponding step as FAIL. However, if multiple elements are expected to be found via the specified locator, then Nexial will poll the current web page until at least 1 element is found or the specified timeout has expired. This is in line with the runtime behavior of Selenium as well.

It is generally not advisable to rely on implicit wait as the only wait strategy. The implicit wait cannot be disabled but it can work very nicely with other wait strategies like Explicit Wait.

Explicit Wait

Certainly an improvement over the implicit wait strategy, this strategy extends the concept of “conditional wait” from the presence of web elements to a sleuth of conditions related to web elements. With explicit wait, we can, for example, delay automation until a certain text is found with a specific web element. Of course, such checks implicitly include the presence of said web element as well. When using this strategy, Nexial instructs the underlying WebDriver to poll and wait until a specific condition is met before proceeding with the intended automation. Nexial implements the explicit wait strategy in 3 different ways:

A. Specific Nexial Commands

Some Nexial commands are implemented to use explicit wait behind the scene. These are the web commands where explicit wait should be used in almost all scenarios:

 goBackAndWait()  refreshAndWait()  selectWindow(winId)  selectWindowAndWait(winId,waitMs)  selectWindowByIndex(index)  selectWindowByIndexAndWait(index,waitMs)  waitForPopUp(winId,waitMs)  waitForTitle(text)

For the list of commands above, the explicit wait is built into the commands themselves. With the commands with a waitMs parameter, one can specify the maximum amount of wait time. For the commands without the additional waitMs parameter, Nexial would use the value of nexial.pollWaitMs as the maximum wait time.

B. Configurable Explicit Wait

Some Nexial commands are configurable to employ either implicit or explicit wait. This potentially gives its users to most flexible way to approach inconsistent or unreliable web applications. Two System variables are at play here:

  1. nexial.pollWaitMs: specify the maximum amount of time to wait. The default value is 30000 (or 30 seconds).
  2. nexial.web.alwaysWait: if set to true then the value of nexial.pollWaitMs would be considered as the maximum amount of time for an explicit wait to expire (and the corresponding step to FAIL). If it is set to false then the value of nexial.pollWaitMs would be considered the maximum amount of time for an implicit wait.

With the setting of a single System variable, many Nexial commands would switch to using explicit wait. This can be an efficient way towards thwarting flaky tests. Some of the commands that are affected by this nexial.web.alwaysWait System variable is shown below (not complete list):

types web commands
assert…  assertAttribute(locator,attrName,value)  assertChecked(locator)  assertCssPresent(locator,property,value)  assertElementByAttributes(nameValues)  assertElementByText(locator,text)  assertElementDisabled(locator)  assertElementEnabled(locator)   assertElementNotPresent(locator,maxWaitMs)  assertElementPresent(locator)  assertElementsPresent(prefix)  assertFocus(locator)  assertLinkByLabel(label)  assertMultiSelect(locator)  assertNotChecked(locator)  assertNotFocus(locator)  assertNotText(locator,text)  assertNotVisible(locator)  assertSingleSelect(locator)  assertText(locator,text)  assertTextList(locator,list,ignoreOrder)  assertTextMatches(text,minMatch,scrollTo)  assertTextPresent(text)  assertValue(locator,value)  assertVisible(locator)
save…  saveAttribute(var,locator,attrName)  saveSelectedText(var,locator)  saveSelectedValue(var,locator)  saveText(var,locator)  saveValue(var,locator)  saveValues(var,locator)
interact  clickAll(locator)  clickOffset(locator,x,y)  deselect(locator,text)  deselectMulti(locator,array)  dragAndDrop(fromLocator,toLocator)  dragTo(fromLocator,xOffset,yOffset)  focus(locator)  mouseOver(locator)  rightClick(locator)  scrollTo(locator)  select(locator,text)  selectAllOptions(locator)  selectFrame(locator)  selectMultiByValue(locator,array)  selectMultiOptions(locator)  selectText(locator)  toggleSelections(locator)  type(locator,value)  typeKeys(locator,value)  updateAttribute(locator,attrName,value)  upload(fieldLocator,file)

One can also consider turning on the explicit wait for a few steps - perhaps in areas that exhibit most of the test flakiness - and then turn it back to default (nexial.web.alwaysWait set to false).

C. Fluent Wait / Wait-Until Commands

In this category, Nexial implements what is known in Selenium as the Fluent Wait across a set of “waitFor” or “waitUntil” commands. The design of these commands is to poll for the desired condition to be met before proceeding further with the automation. As such, this “condition polling” creates a delay effect that allows for the target web application to “catch up”. The “waitFor” commands use the value of nexial.pollWaitMs as the maximum amount of time to wait, while the “waitUntil” commands allow for a time of one’s choosing.

“waitFor” commands:
“waitUntil” commands:

We will be adding more “waitFor” and “waitUntil” commands from time to time.


Conclusion

Different wait strategy serves a different purpose, thus all these wait strategies have its place. While there is no “one-size-fits-all”, the generally accepted best practice regarding wait strategy can be summarized as follows:

  1. Specify a fairly generous value for nexial.web.pageLoadWaitMs to avoid timeout failure when opening an URL. This System variable has no impact on other commands except
  2. Specify true for nexial.web.alwaysWait to enable explicit wait. When invoking commands without the waitMs parameter, the value of nexial.pollWaitMs will be used.
  3. Specify a fairly high value for nexial.pollMs to avoid timeout failure during automation. In the context of explicit wait, setting this System variable with a high value is generally a good idea. For Intranet websites or simple websites, one may start with 15000 (15 seconds). For complex or data-intensive websites, try starting with 30000 (30 seconds).
  4. Avoid the use of Fixed Wait as a delay tactic for the target website to “catch up”. Instead, consider one of the “waitFor” or “waitUntil” commands mentioned in the Fluent Wait section.