How do you deal with Waits in your code?

Iā€™m curious, how do you guys approach Waits before element interactions?

Do you prefer setting up an implicit wait with a global timeout?
Do you prefer the granularity that explicit waits provide, even if it means writing a extra line of code before interacting with an element?
Do you prefer implementing your own methods that interact with elements, encapsulating explicit waits, as well as your own logic, in it?

I know that for some of you this question doesnā€™t make much sense, as there are test frameworks out there such as Playwright that implement their own auto-wait strategies, which works in most cases but Iā€™ve definitely found issues with it, and thatā€™s why I prefer to work with a raw test framework and have absolute control over the test script.

1 Like

I try to avoid them in general.
Playwright has functions that tell the code to wait until the element is in a certain state.
Otherwise, if the automation is too quick for the website, I just add an additional expect case.
I had this scenario where playwright would think it clicked a plus button next to an input field but actually the button would not be in active state (you have to click it to activate it)
But since the input field was not disabled, I clicked the button again before clicking save on the form.

2 Likes

Generally, explicit waits indicate optimization issues with the automation or the infrastructure. one technique I have seen is to gather the whole of the the POM before executing the tests. If the POM is loading slowly then it might even indicate a performance defect. Feel free to ignore me if Im talking out of class :slight_smile:

2 Likes

Unfortunately, frameworks such as Playwright like to pretend that they are flawless, when clearly they are not.
If you do a simple Google Search for ā€œplaywright explicit waitā€, all youā€™ll find is the link to the ā€œAuto-waitingā€ section from their documentation, and that really grinds my gearsā€¦

It was only when I did a little digging into their GitHub Issues section, that Iā€™ve learned that the correct way to wait for a elementā€™s condition is by doing e.g.

await page.locator('locator').waitFor({state: 'visible'})

Which by the way, Iā€™d argue that you should use that wait method instead of the expect() method as thatā€™s to be used for assertions.

2 Likes

Well at least from experience, the WebApps Iā€™ve tested do require some sort of explicit wait in (almost) every interaction with elements.
Given that theyā€™re usually built upon dynamic frameworks like React which arenā€™t as performant, and on top of that they rely on API calls that can arenā€™t instantaneous, testing from point A to point B is never just a matter of click click click.
So yeah, I usually can only complain about the app being slow if itā€™s unbearably slow, to the point it greatly affects the user experience.

Well in my experience even that wait method didnā€™t help in one case.
The scenario was of a side menu opening but apparently playwright interacting with it (filling in the input fields in it) even before it had fully opened. Which meant that when it had to draw a line on a canvas it, failed to do it. Which in my opinion doesnt make sense, how can the code confirm it interacted with a UI element when it isnā€™t even visible.

1 Like

Back in the days with Selenium 1 one of my instant ideas was to implement a polling with a timeout.
Something like this:

waitForElementWithTimeOut(element){
var timeout = 10
var i = 0
while(true){
  if(element.isVisible()){
    break
  } else if (i > timeout){
    throw new Exception ("Could not find $element in $timeout seconds!")
  }
  sleepMillis(1000)
  i++
}
}

I had global/general timeouts which I could overwrite.
The same for the sleep. Some elements should be polled different.

2 Likes

@sebastian_solidwork Iā€™d call that a fused wait: thereā€™s a fuse that metaphorically burns down until the timeout is called while checking for the element to be available.

Iā€™ve built those myself, using the same principles you have - I think itā€™s a pretty common idea.

3 Likes

:raised_hands:
I hope that it is this days more common in frameworks.
Some people really just used sleep() ā€¦

I think technically the method should be called pollingForElementWithTimeout().
ā€˜waitā€™ sounds ambiguous to me.

1 Like

Echoing @katepaulk and @sebastian_solidwork here. I write those kinds of methods to keep from having hard coded waits.

I also like to have a set of constants for general wait times that we can pass in, so something like:

pageLoad = 1000 \\milliseconds
serviceCall = 10000 
longServiceCall = 20000

etc.

That makes it so that if performance requirements change, you only have to update one spot in your code, rather than all over the place.

4 Likes

Nice,
I personally use that approach within my projects.
I have explicit waits and element interactions encapsulated within its own methods e.g.

clickOnElement(locator) {
     waitUntilElementIsClickable(locator)
     findElement(locator).click()
}

Though, I donā€™t know if I like/will keep using this approach.
I initially did this to make the development of tests scripts easier, for whoever uses the project.
But, the disadvantages are that:

  1. you have to create those methods & encapsulate every available element interaction, (click, sendKeys, getText, etc., etc.), and store that big pile of code somewhere (I typically leave it inside the BasePage class);
  2. just as you, I also have set my own default timeouts constants. However sometimes they are not lengthy enough, and sometimes I donā€™t even want to explicitly wait i.e. I more than once faced an issue where the element was clickable, yet the explicit wait was throwing an exception.
    And so because of these, I had to create yet another set of overloaded methods that took a timeout parameter.
  3. they just slow down your code because sometimes thereā€™s just no need to wait for the element conditions e.g. when dealing with/entering keys on a static numpad.
  4. they create an extra layer(s) of complexity between the raw use of the Driver methods, which can create hidden issues. Because not only you have to have faith in the underlying testing framework, you also have to make sure what youā€™ve coded works as expected.

So yeah, Iā€™m still pondering if I should continue with this path. Because Iā€™m just adding unnecessary complexity for the sake of simplicity for the end test programmer.

I donā€™t do it for every element, but for every relevant.
If a certain section is reloaded I wait for the first element to be loaded as indicator that all other elements are loaded too. On the other elements I act directly, without waits.
Itā€™s a try&error process of finding out where are the boundaries, identifying the groups of elements which are loaded/tendered at together.

1 Like