Check out this mini interview i did for Browserstack’s Icon of quality feature, as part of their celebration of International Tester’s day: https://www.browserstack.com/blog/honoring-iconsofquality-corina-pip-qa-lead/
Adding testing to your test automation
Automated tests are supposed to help validate that the product under test is working as expected. For that, a key ingredient is required in your automation: the actual testing. Just as, when you are performing a test case yourself, you are visually inspecting that the expected behavior occurred, so does your automation need to have verification in place. And that verification needs to be implemented by you. Here are some thoughts on this.
waiter2: Useful methods for working with dropdowns and Select
When it comes to selecting values from a classic HTML dropdown, in Selenium you need to perform a few tasks: first, define a WebElement that represents the dropdown as page element; then, using the WebElement, create a Select object; using the Select object, you can then choose an option from the dropdown. This latter option can be done by: visible text (specifying the option’s label), ‘value’ attribute (of the <option> HTML tag found under the <select> tag representing the dropdown, corresponding to which option you want to choose), or by index (position in the list of <option> tags).
Continue reading waiter2: Useful methods for working with dropdowns and Select
Watch me talk about testing with Java, Junit5, Selenium and best practices
Here is a recent recording with some good advice on testing with Java, Junit5, Selenium and best practices: https://www.youtube.com/watch?v=Glrn9jcJCuc. Enjoy.
Watch this Selenium all stars discussion that i am also a part of
Checkout the recording we made with Joe Colantonio for TestGuild, where we talked about Selenium and the upcoming Selenium Conference: https://www.youtube.com/watch?v=uxczD4fgxiQ. Including an all star lineup of Selenium Project members and committers. Enjoy!
waiter2: Typing into fields
When it comes to typing, in the ‘waiter2’ library there are quite a few methods dedicated to this task. But there is a reason why there are so many (32 to be precise): they are different variations, given certain parameters, as the name suggests for each of them. Here are the considerations, whose combinations will give the method signatures:
waiter2: Clicking on page elements
Clicking on a page element is easy to do, however sometimes this action might fail. This can be either because the element we want to click is not yet present on the page, or is not visible, or does not yet allow interactions. To help make sure the click succeeds, here are my dedicated wait based click methods.
Clicking a page element: click
There are 4 methods for clicking on a page element in the ‘waiter2’ library: 2 of them take the timeout parameter, 2 don’t. Additionally, 2 of them take a WebElement (defined with @FindBy) as parameter, 2 take a By variable:
public void click(WebElement elementToClick) public void click(WebElement elementToClick, int specificTimeout) public void click(By selectorForElementToClick) public void click(By selectorForElementToClick, int specificTimeout)
It is worth mentioning that there will never be a ‘waitForElementToBeDisplayed’ method in the library, simply because it is not needed. For example, when trying to click on a page element, there is no need to wait for it to be displayed before clicking. The logic inside the ‘click’ method from ‘waiter2’ retries clicking the element any time an Exception is encountered. If the element would not be present, the NoSuchElementException would be thrown for each click, until it either is displayed, or the timeout value has elapsed. Similarly, if the page element were present but not visible, or not clickable, the click action is tried until it succeeds or the timeout elapses. Any Exception that can be encountered while clicking is treated the same, because the generic ‘Exception’ is checked for and treated.
So basically, calling the click method while the element to be clicked is not yet ready, but in the process of being ready, will reliably click on it (given it loads within the specified timeout).
In case the element is not clickable for any reason within the specified timeout, a RunTimeException is thrown, with the message:
"waiter2.FAILURE: Could not successfully click on '" + elementToClick + "' within " + specificTimeout + " seconds."
The provided selector will be displayed, so that the tester knows what selector was looked for. This helps debug the failure.
Since version: 1.0
The code
The ‘click’ method variants that take a WebElement as parameter are:
public void click(WebElement elementToClick) { click(elementToClick, TIMEOUT); }
public void click(WebElement elementToClick, int specificTimeout) { WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout)); try { ExpectedCondition<Boolean> condition = arg -> { try { elementToClick.click(); return true; } catch (Exception e) { return false; } }; wait.until(condition); } catch (TimeoutException e) { throw new RuntimeException("waiter2.FAILURE: Could not successfully click on '" + elementToClick + "' within " + specificTimeout + " seconds."); } }
The ‘click’ method variants that take a By as parameter are:
public void click(By selectorForElementToClick) { click(selectorForElementToClick, TIMEOUT); }
public void click(By selectorForElementToClick, int specificTimeout) { WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout)); try { ExpectedCondition<Boolean> condition = arg -> { try { driver.findElement(selectorForElementToClick).click(); return true; } catch (Exception e) { return false; } }; wait.until(condition); } catch (TimeoutException e) { throw new RuntimeException("waiter2.FAILURE: Could not successfully click on '" + selectorForElementToClick + "' within " + specificTimeout + " seconds."); } }
Usage examples
The best usage for the ‘click’ method would be: providing a WebElement defined in a page object class with an @FindBy annotation. For example, for a page on which there is a button to click, whose id is “buttonToClick”, the corresponding WebElement is:
@FindBy(id = "buttonToClick") public WebElement buttonToClick;
Don’t forget that as per the setup, you need to do a PageFactory.initElements before using the WebElement. In the test, in order to click on this page element, you would just need to pass the WebElement as parameter to the ‘click’ method (shown here with the default timeout value):
waiter.click(page.buttonToClick);
In case you cannot create the WebElement through the @FindBy annotation, because it gets generated dynamically in the test, you can pass a By variable to the ‘click’ method. There are 2 ways you can do that: either by creating a By variable somewhere in the test and passing it to the method once it has the correct value. Or by creating a String variable somewhere in the test that represents the selector, then passing it to a By.yourSelectorType once it gets assigned the correct value. Below are some examples for the same button as above.
If you want to create a By variable, it would look like the following one (here obviously it represents an id):
By byForButtonToClick = By.id("buttonToClick");
You can now use it in the ‘click’ method:
waiter.click(byForButtonToClick);
If instead you want to create a variable only for the String representing a selector:
String selectorForButtonToClick = "buttonToClick";
Then, to click the corresponding element:
waiter.click(By.id(selectorForButtonToClick));
Note1: If the selector value is static (it is always the same), use the @FindBy approach. Only use the By approach if you cannot use the @FindBy one, due to not knowing at the beginning of the test what the selector will be (as it gets generated along the way).
Note 2: If you are using the By or the corresponding String only to click that item once, and not anywhere else in the test, DO NOT create a separate variable to store these. Just pass them inline, directly into the ‘click’ method, like, for example:
waiter.click(By.id("buttonToClick"));
In case the element specified with @FindBy cannot be clicked due to any reason within the specified timeout, the following RunTimeException is thrown:
java.lang.RuntimeException: waiter2.FAILURE: Could not successfully click on 'Proxy element for: DefaultElementLocator 'By.cssSelector: nonExistentElement'' within 30 seconds.
In case the element specified with By cannot be clicked on due to any reason within the specified timeout, the following RunTimeException is thrown:
java.lang.RuntimeException: waiter2.FAILURE: Could not successfully click on 'By.cssSelector: nonExistentElement' within 30 seconds.
Incorrect usage!
Never do the following:
WebElement buttonToClick = driver.findElement(By.id("buttonToClick")); waiter.click(buttonToClick);
Why? Because if the button to click is not displayed when the ‘findElement’ method runs, the test fails at that step, and the ‘click’ method never executes.
So that’s it for clicking on page elements, which of course, include more than just a button. The test code can be found here. Next up i will discuss some further useful methods, so stay tuned!
waiter2: Opening a page and waiting for it to load
In this post I will describe what the get, wait for page load and wait for jquery to load methods from the ‘waiter2’ library do, and how to use them in your tests. For setup details, please read the previous post.
Waiting for a page to load: waitForPageLoadComplete
There are 2 methods with the name ‘waitForPageLoadComplete’. The first one does not take any parameters. The second one takes an int as parameter, corresponding to how many seconds to wait for the page to finish loading successfully. I will refer to the no parameter variant in the following paragraph.
This method simply waits for the page document and corresponding static resources to finish loading for up to 30 seconds (the default timeout set in the method). The method uses the ‘document.readyState’ property to check for this. When the value of this property is ‘complete’, the wait method exits successfully. If the condition is not met within the specified timeout, the method throws a RunTimeException, with the following message: “waiter2.FAILURE: Page did not finish loading within 30 seconds.”. This message should make it easy to identify, in the test output, why the test failed (in case the page did not load successfully during the specified timeout period).
Since version: 1.0
The code
The code of this method is:
public void waitForPageLoadComplete() { waitForPageLoadComplete(TIMEOUT); }
This method actually calls the method variant that takes a specified timeout as parameter. It sets that timeout to the default value of 30 seconds, defined in the Waiter class through the TIMEOUT constant. This latter method is:
public void waitForPageLoadComplete(int specificTimeout) { WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout)); try { wait.until(driver -> String .valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState")).equals("complete")); } catch (TimeoutException e) { throw new RuntimeException("waiter2.FAILURE: Page did not finish loading within " + specificTimeout + " seconds."); } }
Note: ideally you wouldn’t use this method directly. It is called from a method described below for opening a page (called ‘get’). Normally you would wait for a page to load after opening it.
Usage examples
Inside a test, once the waiter class was initialized and a driver instance was created, calling this method is simply done as:
waiter.waitForPageLoadComplete();
Waiting for jQuery to load: waitForJQuery
In order to wait for jQuery to finish loading, one of the 2 variants of the waitForJQuery method can be used: one without passing the timeout value as parameter (whose default timeout is 30 seconds) and another one that takes an int timeout value. If there is no jQuery on the page, the method exits immediately, successfully. Otherwise, in case it is present, the jQuery.active property is waited for, up to the defined timeout, to equal 0.
Since version: 1.0
The code
The method without parameters is as follows (and calls the other variant of the method, passing to it the TIMEOUT constant to specify 30 seconds as timeout):
public void waitForJQuery() { waitForJQuery(TIMEOUT); }
The variant which takes an int parameter for the timeout is as follows:
public void waitForJQuery(int specificTimeout) { WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout)); try { ExpectedCondition<Boolean> condition = arg -> { try { return (Boolean) ((JavascriptExecutor) driver).executeScript("return jQuery.active == 0"); } catch (Exception e) { return true; } }; wait.until(condition); } catch (TimeoutException e) { throw new RuntimeException("waiter2.FAILURE: JQuery did not finish loading within " + specificTimeout + " seconds."); } }
Note: like with waitForPageLoadComplete ideally you wouldn’t use this method directly. It is also called from a method described below for opening a page (called ‘get’).
Opening a page and waiting for it to load, together with jQuery: get
The ‘get’ method will be used for: opening a URL in the browser, followed by waiting for the page to load (by calling the waitForPageLoadComplete method), followed by waiting for jQuery to finish loading if present (by calling the waitForJQuery method).
For ‘get’ there are also 2 variants: one without a timeout parameter, and one to which you can pass an int for the timeout. For the version without the timeout parameter, the method will wait for up to 30 seconds for the page to load after opening the URL (which is done by calling Selenium’s ‘get’ method), then for up to 30 seconds for jQuery. So in total there will be a wait of up to 60 seconds. The other parameter taken by the method is a String that represents the URL you want to open.
Since version: 1.0
The code
The variant of the ‘get’ method with default timeout is (this method calls the one described right below it):
public void get(String url) { get(url, TIMEOUT); }
The variant of the ‘get’ method to which you need to pass an int timeout value is:
public void get(String url, int specificTimeout) { driver.get(url); waitForPageLoadComplete(specificTimeout); waitForJQuery(specificTimeout); }
Usage examples
Due to the methods called inside the ‘get’ method, in your tests you never have to call those methods yourself. Here of course I am talking about the wait for page and jQuery to load ones. This helps reduce the code you write in each of your tests, since otherwise you would have to write 3 lines of code every time you opened a page. Now you just need to write 1. To open a valid URL, just pass in the valid URL, and either no timeout value or a desired int value, representing seconds. For the first case, you would proceed as follows:
waiter.get("https://www.selenium.dev/documentation/");
Or, to wait for 60 seconds instead of the default 30:
waiter.get("https://www.selenium.dev/documentation/", waiter.MEDIUM_TIMEOUT);
So that’s it regarding the methods for opening and waiting for the page to load. The test code can be found here. Next up I will discuss some further useful methods, so stay tuned!
waiter2: My new Selenium wait based library
Some years ago, at a conference, I announced that I released a Selenium based library that contained useful wait-based methods for test automation. After a while I archived that project as I did not find time to work on it too much. Well, now, years later, having gathered new ideas, having a refined vision of what that library should be, and with some fresh energy, I am releasing the, let’s say, V2 version of it. In fact it is a brand new library, bigger and better, and it’s name is: waiter2.
I wanted to specify the ‘2’ in the name to highlight that this is based on the original idea (‘thewaiter’) but it is an improved concept. I kept the ‘waiter’ name as the library contains mostly methods that use Selenium’s WebDriverWait mechanism to ensure reliability in tests. It is a Maven based Java project with a Selenium dependency.
The What
I will be making several small releases that will contain the new methods I am interested in, instead of a single large release. I will track progress of that here, on the blog. Each set of new methods released will have an associated blog post where I will describe what is new, and how to use the newly released methods. I will also specify the library version in which a feature will be available. Additionally, the code will be well documented through Javadoc.
So what functionality will I cover with the new methods? Well, you will have the possibility to click on items, work with checkboxes or dropdowns, read element texts or attribute values, work with iframes or multiple windows. Basically whatever I will consider useful, I will add to the library.
Importing into your project
The library will be available for download from the Maven Repository website, at: https://mvnrepository.com/artifact/com.imalittletester/waiter2. It is recommended to always use the latest available version, since it always contains the most features. Add the dependency as it is shown in mvnrepository to your project’s pom.xml file, in the <dependencies> section. E.g.:
<dependency> <groupId>com.imalittletester</groupId> <artifactId>waiter2</artifactId> <version>LATEST-VERSION</version> </dependency>
This library has as dependency the latest released version of Selenium, which means you don’t need to separately import it. However if you want to manage which Selenium version is imported into your project, you will need to exclude it from the ‘waiter2’ dependency, using an ‘exclusions’ section.
Library contents
There is only one class in the library, called Waiter, in the main -> Java -> utils package. It contains a field, named ‘driver’, and a constructor. It takes the driver instance from a test that initializes the Waiter class and sets it to the Waiter class field. This way you can call all methods from the Waiter class without having to pass the driver instance to each method. The driver instance is used inside the wait methods.
Most of the methods I implemented in the library use the WebDriverWait functionality. Initially, a WebDriverWait variable is created, to define up to how many seconds to wait for the condition specified in the method to happen. Then, the condition is defined. Every half a second the condition is retried, until it is fulfilled successfully, or the timeout has elapsed.
In the same Waiter class you can find a few constant values, whose names contain ‘TIMEOUT’. These are used either inside the methods to set for how long a method waits for a condition to be true, or they can be used by you in tests when calling methods that take a timeout value as a parameter. Each wait method comes in 2 variants: one without a timeout parameter and one with a parameter which specifies a timeout value. The TIMEOUT constants are ints as follows: TINY_TIMEOUT of 10 (corresponding to 10 seconds when passed as parameter to wait methods), TIMEOUT of 30 (the default value unless you provide a timeout parameter), MEDIUM_TIMEOUT of 60 and LONG_TIMEOUT of 120. I did not provide larger numbers, since waiting for more than 2 minutes for any UI event is simply too much.
When it comes to methods that require page elements to be specified as parameters, these also come in 2 flavours: methods that take a WebElement specified through a @FindBy annotation, and the corresponding methods that take a By item.
The methods are written so that if there is a failure due to a condition not being met within the timeout period, instead of a TimeoutException you will get a RunTimeException, with a concise message regarding the failure. This makes it easier and faster to figure out, by looking at a test failure, why the test failed.
The GitHub location of the library is: https://github.com/iamalittletester/waiter2.
Test setup
In order to call the methods from the ‘waiter2’ library, I recommend having a dedicated variable in the test class. I would call it ‘waiter’. In order to initialize the Waiter class (from the ‘waiter2’ library), a driver instance needs to exist. It needs to be passed as parameter to the constructor of the Waiter class. My preferred setup would include initializing the driver instance in the @BeforeAll method (or @BeforeClass, depending on whether you use JUnit5 or TestNG), then initializing the ‘waiter’ variable:
waiter = new Waiter(driver);
In a nutshell, this is what the ‘waiter2’ library is about. In the next posts I will discuss the methods I implemented in it, and how to use them, so stay tuned!
My articles published in September
This September was quite productive, as i had 4 test automation related articles published, which i recommend reading:
- Creating an Architecture for Your Automated Tests
- Writing Automated Tests in Small Increments
- 4 Times Your Automation Passed While Bugs Were Present
- Now That You’ve Created Automated Tests, Run Them!
Enjoy.