Checking page state in Selenium test automation
I wrote a medium length novel in response to Nikolay Advolodkin's post about a common Selenium automation pattern. He advocates:
STOP CHECKING IF PAGE IS LOADED IN AUTOMATED UI TEST
You can read his article on his site, Ulimate QA: https://ultimateqa.com/stop-checking-if-page-is-loaded-in-automated-ui-test/
Or follow the discussion on his linkedin post: https://www.dhirubhai.net/posts/nikolayadvolodkin_testautomation-java-selenium-activity-6674278585743761408-Ud_p
Nikolay is a good friend, so please don't take this as an attack. We have had many long discussions like this.
Here is my response in full:
I go the other direction on this. I use element checks to verify that the page is loaded.
My version of the Page Object pattern includes an isLoaded() method -- which can be overloaded with custom checks as needed. This is to try to keep things synchronized, even though it means extra steps. In this case, I value stability over performance.
I can understand someone making another decision however, especially when speed is important and latency between steps with a remote driver makes this more costly.
In practical terms, you could just check if the element you want to interact with is available and fail faster if it is not. The result of both success and failure would be the same, and you'd get there slightly faster -- perhaps significantly faster if you have a long sequence of many page loads. But having such long test flows is a pattern I try to avoid, unless I'm explicitly testing the long flow through the UI.
Adding the sanity check helps me when it is time to debug the test or analyze the test failure. Knowing that my page is not loaded -- or that I'm not on the page I expected helps me to understand the true cause of failure, the page is not loaded, rather than an element is not clicked.
However, I would not call isLoaded() repeatedly, only once, automatically when a page object is initialized, or explicitly if I have a logical reason to think that it is not -- some state change.
Selenium tests (and UI tests in general) are brittle, and determining state before attempting to perform an action is one of the biggest challenges.
The challenge here is that an HTTP 200 status code doesn't really mean a page is loaded anymore, with dynamic page creation, javascript frameworks, single page apps, and prefetch, this is hard to tell. Pages can load in chunks, dynamic elements can be added, and sometimes the concept of a "page" doesn't even make sense.
Checking status codes or XHR ready state are meaningless (or at least misleading) in many modern web applications. But you see people trying to do this figure out the state of their app, so they can reliably automate it. This usually doesn't work. So checking the state of the element you need to interact with makes more sense as well as saving time.
The WebDriver team dropped the ball on this -- or at least punted. Selenium used to check that a page was loaded (using the methods above) but decided the decision was too complex and left it up to the user. I think this was an abrogation of responsibility -- but don't tell Jim or Simon that. It's a less discussed detail of their least favorite topic.
Validating state is hard, and most of the time, leaving it up to the user results in bugs. It's even harder with mobile apps and the Appium team has had to make many difficult decisions about this, and sometimes a framework gets it wrong, or makes things unnecessarily slow.
So like most things there is a trade off between speed and reliability, and we all need to make our own decisions.
When you adopt the "page object" pattern for use on components that are only on part of a page, or may appear on multiple pages, having the explicit check that is user defined starts making even more sense -- because widget.isLoaded() is a state check that can happen more than once, and not just a sanity check
But when you have a (reasonably) static page, an initial check that the page is loaded -- rather than checking each element that you can safely assume *should* be there if the page is loaded can actually be more performant, as well as providing a clearer stack trace when things aren't as expected.
Repeatedly checking if a page is loaded before performing any action is a bad idea in any case.