Tag Archives: webdriver

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!

Browser unaware Selenium tests. STEP 3: Starting a browser based on a system property

By now, following the previous two posts in this series, you have setup the methods that initialize a Chrome and a Firefox browser. In this post, you will see how to use System properties for easily switching the browser in tests. Continue reading Browser unaware Selenium tests. STEP 3: Starting a browser based on a system property

Browser unaware Selenium tests. STEP 2: Creating the browser initialization methods for Chrome and Firefox

In this second post of the ‘browser unaware Selenium tests’ series, i will show what the methods that start Chrome and Firefox look like, based on the selected OSs from the previous post. Continue reading Browser unaware Selenium tests. STEP 2: Creating the browser initialization methods for Chrome and Firefox

Browser unaware Selenium tests. STEP 1: Identify OSs on which to run tests + choose browsers to support

In this blog post series, i want to show how i normally set up my browsers and my Selenium code, in order to enable writing ‘cross-OS’, ‘cross-browser’, ‘browser-unaware’ tests. What this means: my tests can run on any OS i set up seamlessly; each test can be run on multiple browsers seamlessly; the tests do not care and are unaware of what browser they are running on. They just run and do their job, being focused on what needs to be tested, rather than on how to setup the browser or OS. OS specific setup will be done on the project level and in the class where browser initialization is defined, whereas the browser the tests will run on can be switched easily by using a configuration. This post series does not focus on using Selenium’s grid.

1.1. Have a Java project available

The first step in creating any sort of automated tests is to create an infrastructure where to gather all your test code. Meaning you need to have a code project, in which to perform all the setup you need and where to store your test code, test files, utilities code and files, and any other resource you might need in order to run the tests that you want.

All the code demonstrated in these posts is stored in a Maven project called ‘selenium-tutorial’. I will not go over what Maven is, or how to use it, but instead i will provide their website, for you to read about it in case you are new to Maven: https://maven.apache.org/index.html. The Maven project can be downloaded from its Github location: https://github.com/iamalittletester/selenium-tutorial

A few words about how the demo project is setup: being a Maven project, upon creation a default structure was created for it, structure which is reflected in folders on the computer where you store the project. A few of these folders and their purpose are described below:

  • src → main → java: here you will find utility code, which is not test code but is used in the tests. In this project, in this folder, you will find several other folders, that group the utilities based on their purpose. One of these folders, ‘browser’, will contain the class where all the browser initialization and shutting down is stored. 
  • src → test → java: this is where the tests will be stored, each in their corresponding folder. The folders help group tests by functionality or feature under test, so that you can easily find them.
  • src → test → resources: here, in the ‘browserBinaries’ folder, the driver binary files, described in this post, are stored, which are used for interacting with the web browsers.

1.1.1. Importing the required dependencies

In order to create and run Java Selenium tests, some external libraries need to be imported into the project. Since the examples in this post are from a Maven project, in its’ ‘pom.xml’ file, the following dependencies are declared (replace the values from the <version> tag with the latest ones available in the Maven Repository):

<dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>${selenium.version}</version>
</dependency>
<dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>${commons.version}</version>
</dependency>

All things Selenium are accessible due to the import of the ‘selenium-java’ library. The remaining import, ‘commons-lang3’ is used for other operations in the test project, like helping determine the OS on which tests are running. It is a library which contains a collection of helping methods, like processing of Strings, generating random Strings, easily working with Files, and so on.

1.2. Choose the OS to support

A decision you need to make is what OSs you want to be able to run your tests on. You don’t need to decide at the beginning, when you are creating your test framework, as you can add to the list of supported OSs as you progress in your testing. This is because the setup for a newly supported OS is not complicated and does not take too much time to accomplish. 

When you decide on what OSs to run the tests on, think about who will run these tests: you or some colleagues of yours, or maybe they will be run on a remote machine, by a Jenkins runner.

It is important to know what these supported OSs for the tests are, because in the setup process you will need to use some files called ‘drivers’ (described in subchapter 1.4.) for allowing browser interactions. These are specific to each OS. What you don’t need to concern yourself with is which version of the OS you will use. For example, just think about whether Windows is supported, instead of considering whether Windows 10, 8, 7, XP and so on will be needed.

In these blog posts, for the sake of example, the supported OSs will be considered: Linux, Mac, Windows.

1.3. Choose the browsers to support

Based on the supported OSs, you can also choose which browser to support on each of them. Some browsers will of course work on only one OS (Internet Explorer), whereas some have cross-OS support (like Chrome or Firefox). This again is needed for knowing which browser drivers need downloading. 

For each combination of OS + browser, you will need to download a corresponding binary file. That will be discussed in the next subchapter.

For the purpose of example, in these posts, the following browsers will be supported: for Windows: Chrome, Firefox; for Linux: Chrome, Firefox; for Mac: Chrome. The setup for each of these combinations of OS and browsers will be described further below.

1.4. Download the drivers for the supported browsers

1.4.1. What are the drivers

When you are interacting with a browser by means of a Selenium test, you are not directly giving commands to the browser. An intermediate step exists in your communication with the browser, which involves a file called a ‘driver’. The driver will receive the commands you want to send to the browser, and forward these to the browser. The response from the browser based on the commands you sent will also be received by the driver and forwarded back to your tests. Simply put, the communication between the test code and browser is: test → driver → browser (when issueing a browser interaction command) and browser → driver → test (for receiving the response of the interaction command).

The driver files are specific to each browser and the operating system they are running on. In order to run Selenium tests, you will need to download the driver files corresponding to the browser/operating system you are aiming to test on.

1.4.2. Where to download the drivers from

Each browser binary is maintained by different projects, as you can see on the official Selenium documentation page, the ‘Consumer browsers’ and ‘Specialised browsers’ sections: https://www.selenium.dev/documentation/en/getting_started_with_webdriver/browsers/. For the purpose of these blog posts, the Mozilla driver, called ‘geckodriver’, and the Chrome driver are downloaded and setup in the project. Their download locations are:

From each url you can download all the released versions of these drivers. However you need to take into account browser-driver compatibility: each driver version works with only specified browser versions. Ideally always use the latest driver version and latest browser version. In some cases, due to constant browser upgrades, you might encounter some strange errors in your tests, due to the fact that the latest browser you installed does not work with the driver you have in your project. If such a situation occurs, simply visit the driver download location and download the latest version. If you are already at the latest version, constantly go back to the download page, as the maintainers are surely working on a new version which will be compatible with your browser.

At each of these download locations, you will find the driver files corresponding to the Windows, Linux and Mac OSs. For some versions, you will also have the possibility to choose between driver files for 32 or 64 bits operating systems.

In order to have your tests run on any machine, the driver files should be stored in the test project (instead of a random location on your machine which then needs to also exist and contain these files on all machines where the tests will run).  The suggested location for storing the driver files is a dedicated folder in the ‘src/test/resources’ location in your project. In the test project used across this post, the driver files are stored in the following location: src/test/resources/browserBinaries. This location will need to be specified in the browser initialization methods described in the following blog post.

For the examples in this post, the following drivers were downloaded: Chrome driver for Windows, Linux and Mac, and Mozilla geckodriver for the same OSs. Now, since the driver files for Linux and Mac have the same name for Chrome, the one for Mac will be renamed to ‘chromedriverMac’. Similarly, for geckodriver, the filename for the Mac driver will be changed to ‘geckodriverMac’. Therefore, the setup for this post contains, in the src/test/resources/browserBinaries folder, the following files: chromedriver.exe (for Windows), chromedriver (for Linux), chromedriverMac (for Mac), geckodriver.exe (for Windows), geckodriver (for Linux) and geckodriverMac (for Mac). The Windows and Linux versions of these files are those for the 64 bit operating systems.

In the following blog posts in this series you will see how to set up the browsers and how to open them, but also how to make sure you don’t hardcode them in tests.

thewaiter: wait for WebElement attribute. To equal, contain a String, with variations.

An attribute of an HTML tag (or WebElement as you might know it from Selenium) stores valuable information about the state of that element. If we are thinking of checkboxes, a “checked” attribute will signal whether the checkbox is selected or not. For a link, the “href” attribute will tell us what location on the web it points to.

There will be times when your Selenium tests will need for an attribute of a WebElement to have an expected value. This signals that the state you expect your product to be in is correct. Before any other steps will be performed, you will need to make sure that the value of the attribute is correct, and for this process asserts are quite frequent. Enter “thewaiter” library, which has methods for you to wait for the attributes, not only to equal a text, but to also contain it, or to equal/contain it ignoring whitespaces or the case. Continue reading thewaiter: wait for WebElement attribute. To equal, contain a String, with variations.

Use waits as assertions for your Selenium tests

Selenium tests tend to make a lot of use of assertions, to check that some actions have been performed on the front-end or that some WebElement properties are the expected ones. And by assertions, I mean, mostly: assertEquals or assertTrue, as these are the most commonly used ones.

Assertions fail too often due to the condition they are checking not being fulfilled by the time the assertion code executes. Had the assertion run a few seconds later, in many cases, it would have passed. It’s all about timing. In my previous posts I described some of the WebDriverWait based methods you can find in my ‘thewaiter’ library. In this post I want to highlight how you can use these methods to replace your assertions. Continue reading Use waits as assertions for your Selenium tests

thewaiter: waiting for a URL in the browser to equal or contain a String, with Selenium

In some tests it is not enough to just wait for a page to fully load, but instead you need to make sure that the URL corresponding to that page is the expected one. Maybe you clicked on a button and need to make sure an expected page/URL opened, or maybe you are opening a page but a redirect changes the URL to something else than the initial page had. For such tests you can use the URL related wait methods from ‘thewaiter’ library, to wait until the URL is the correct one, before performing the rest of the test. Continue reading thewaiter: waiting for a URL in the browser to equal or contain a String, with Selenium

thewaiter: clicking on an element by using waits with Selenium

One of the most common ways of interacting with a page displayed in a browser, in Selenium tests, is clicking on a WebElement. But many times, due to the timing when the click happens, it will fail, since the WebElement that needs to be clicked is not yet available. This might be because some Javascript events have not finished enabling that WebElement, or other similar issues. Making clicks reliable can be done by using WebDriverWait, to wait until the click can actually happen.

Continue reading thewaiter: clicking on an element by using waits with Selenium

thewaiter: opening a page and waiting for it to load with Selenium

In this post i will discuss the methods you can find in thewaiter library, for waiting for a page to load completely. I will show the methods you can use from thewaiter that can help you with these waits, and some test examples for each, that you can also find in GitHub.   Continue reading thewaiter: opening a page and waiting for it to load with Selenium

Introducing thewaiter. A WebDriverWait based library for writing reliable Selenium tests

During my talk at the recent SauceCon conference, i described how you can write reliable Selenium tests by using WebDriverWait based methods to wait for page events to take place. I am now happy to announce that my little project, thewaiter library that i mentioned in the talk, is available to use from the Maven Repository! Read on for details. Continue reading Introducing thewaiter. A WebDriverWait based library for writing reliable Selenium tests