Now I decided to share the demos and you can find it here.
Each demo is contained in a Test Case method. Most of them use a specific super-simple HTML file that is part of the project too. Note that not all tests pass, and this is by design!
Here’s a short description of the test cases:
- IsItABugInSelenium – If you just open the file IsItABugInSelenium.html in the browser, it loads fine. If you run the test, you’ll get a general WebDriverException error that many people think of as an internal Selenium problem. However, if you’ll try to click the button on the page manually, you’ll see that the entire browser tab hangs, regardless of Selenium. Open the html file in a text editor and see why…
- DoWeReallyNeedWait – Open the file SlowButton.html in the browser and click on the button. You’ll see that the input box is updated only 3 whole seconds afterward. Note that the test does not use any sort of waiting (no Thread.Sleep, no WebDriverWait, no ImplicitlyWait, etc.) between the press of the button and the verification of value in the input box, but if you run the test, you’d probably be surprised to see that it passes! Again, look inside the html file to see what it does.
- WillThisHelp – This example is identical to the previous one, except of that line that was added at the beginning of the test:
This should solve the problem, isn’t it? Well, if you run the test you’ll see that it doesn’t. The reason that it doesn’t is that ImplicitlyWait only affects finding elements (i.e. the methods IWebDriver.FindElement and IWebDriver.FindElements), but the test fails because GetAttribute(“value”) doesn’t wait for the value to change. If you think about it for a moment, it makes sense because Selenium can’t know when the value is actually ready…
- ExplicitWait – This example shows how to actually solve the problem of the previous test by using an explicit wait (using the WebDriverWait class).
- WaitAndIgnoreExceptions – This example shows two things regarding WebDriverWait. The first is that it can be used to perform any operation regardless of Selenium. In this example we wait for a file to be created. The second this is the IgnoreExceptionTypes method. To see this test in action, before running the test, open Notepad, type “Hello” and save it as C:\temp\test.txt, but leave Notepad open. Then run the test and notice that it’s running doing nothing. Only when you click Save in Notepad the test completes. Explanation: first the test deletes the file to ensure that it doesn’t exist; then it uses the WebDriverWait to read the content of the file. While the file does not exist, the lambda passed to the WebDriverWait is called but throws FileNotFoundException. But because we specified that type of exception in the IgnoreExceptionTypes, WebDriverWait continues to invoke the lambda continuously until you re-create the file through Notepad. WebDriverWait keeps invoking the delegate that you pass to it until it doesn’t throw an exception and returns something that is not null or false.
- ObviousNoSuchElementException – This example starts the discussion on the various Selenium exceptions, specifically NoSuchElementException and StaleElementReferenceException. It shows that obvious fact that if an element does not exist, a NoSuchElementException is thrown. Pay attention that this exception is thrown from the FindElement method and not from the Click method (this is still quite obvious…)
- WhenImplicitlyWaitHelps – In this example we finally see when ImplicitlyWait really helps (unlike in the WillThisHelp test). The point to notice here that clicking the button sets a timer that creates a new element when it’s due, and that we don’t have any explicit wait before we use FindElement to find the new element. You can try to comment out the first line, which uses the ImplicitlyWait and see that without it the test fails.
- StaleReferenceElementDemo – Many automation developers hate, or afraid from this exception, because they don’t fully understand what it means. Here’s a very simple example that cause it: the button in RemoveElement.html removes the input element from the DOM. In the test, we find the input element before we click the button, so FindElement succeeds. However, after clicking the button, the input element disappears and therefore any method or property that we invoke on the input element will consequently fail with StaleElementReferenceException.
One important thing to note: StaleElementReferenceException is never thrown from invoking methods on the IWebDriver (e.g. IWebDriver.FindElement), only from methods on IWebElement (though IWebElement.FindElement may throw it).
Other cases that this exception can occur is when you try to use an element that you found on one page after you already navigated to another page; or when you found an element on one iframe or window, but used SwitchTo to switch to another iframe or window. This is because an iframe is essentially another page.
- WhatHappensWhenFindElementsFindsNothing – Before running this test, try to answer this question: what happens when FindElements (note the plural form!) finds no matching element?
- Throws NoSuchElementException
- Returns null
- Returns an empty list (IReadOnlyCollection<IWebElement>)
If you’d run the test, you’ll see that the correct answer is 3
- FindElementsAndExplicitlyWait – Now, what do you think would happen if you’re using FindElements after you used ImplicitlyWait? There are 2 cases to consider: the first is what happens if there are some matching elements when the method is called?
- It returns immediately with the elements that it found
- It waits the duration specified in the ImplicitlyWait anyway, in order to allow for additional elements to be added during this period (e.g. if a list is loaded asynchronously)
The second case is when there are no matching elements when the method is called?
- The method will immediately throw NoSuchElementException
- The method will immediately return an empty list
- The method will wait until at least one matching element is found and then return (even if the specified during did not elapsed). If it doesn’t happen, it will wait for the entire duration and then will throw NoSuchElementException when the duration elapses.
- The method will wait until at least one matching element is found and then return (even if the specified during did not elapsed). If it doesn’t happen, it will wait for the entire duration and then return an empty list.
- The method will anyway wait for the specified duration and only then return with whatever elements it found, or an empty list if no element was found.
The answers to this tricky questions are 1 and 4 (even though in my opinion it should have been 1 and 2, because if the page loads the elements in a list asynchronously, then finding one element doesn’t mean that the entire list was loaded. Instead, I’d prefer to use an explicit wait with some external indication that will tell me if the list was completely loaded or not. But they won’t change it now even if they’ll think I’m right, as it will break backward compatibility…)
- WasTheButtonReallyClicked – To understand what’s going on with this demo, open the page Calculate.html in the browser, and also look at its source. As you can see, after you type two numbers in the X and Y fields and press Tab to move the focus away from the Y field, it invokes the displayExpression function which displays the corresponding expression. Only when click the “Calculate” button, it should also write the result. But if you try this out you’ll see that apparently clicking the “Calculate” button does nothing. This is true also when you run the test using Selenium.
The other C# source file, InvestigateFailures.cs, continues the narrative of the failing Calculate page, and shows some techniques that help investigate failures:
- TakeScreenshot – shows how to take a screenshot
- LogEvents – this shows the use of the EventFiringWebDriver to automatically write every click to the log. You can of course use this class to write all other supported events to the log too. Unfortunately, some events, e.g. ElementValueChanging does not provide all the important information like the previous and new values. In fact, I submitted a pull request to fix this, but unfortunately I didn’t get any response yet…
Let me know if you’ve learned a think or two from this article, or if you have any other comment!