Click here to Skip to main content
15,887,596 members
Articles / Programming Languages / Java
Article

Running Automation Tests at Scale Using Selenium

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
9 Nov 2023CPOL12 min read 4.4K   4   1
In this article, we will learn how to set up a Selenium WebDriver with Java and run the web automation tests (in series and parallel) on the LambdaTest cloud grid.

This article is a sponsored article. Articles such as these are intended to provide you with information on products and services that we consider useful and of value to developers

Selenium WebDriver is an open-source, cross-platform library designed to help automate browser testing. It is used by test engineers to perform automation testing by interacting with web browsers and different elements on the web page to simulate the user actions on websites.

The introduction of Selenium Manager in Selenium WebDriver version 4.6.0 has brought a big relief to the test automation engineers as it has the batteries included.

This way, Selenium Manager does not require setting the driver executable path or using third-party libraries like WebDriverManager to start the browsers.

With the release of Selenium WebDriver 4.12.0, more features have been included, like Automated Driver Management and Automated Browser Management for Chrome and Firefox browsers. That said, if you don’t have the browsers installed on the machine where tests are run, Selenium will install the browser, download the required browser drivers, and run the tests. Awesome, isn’t it?

Selenium WebDriver with Java has a huge community, so automation testers can make use of active community contributions and detailed documentation to write tests. It can also be useful in providing quick help in case the testers get stuck while automating the websites.

In this blog, we will learn how to set up a Selenium WebDriver with Java and run the web automation tests (in series and parallel) on the LambdaTest cloud grid.

So, let’s get started!

Setting up the framework

Before we look into the demonstration, let’s first organize a few things that we will need further to write the code. For instance, programming language, framework, and more. In this case, we will use Java as our programming language, TestNG as our testing framework, and any IDE of your choice.

Programming Language/ Tools/Framework Version
Java 17
Selenium WebDriver 4.12.1
TestNG 7.8.0
Maven 3.9.4

To start with, we need to create a new Maven project in the IDE. After successfully creating the project, let's add the Selenium WebDriver and TestNG dependencies in the pom.xml file.

File Name: pom.xml

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>io.github.mfaisalkhatri</groupId>
   <artifactId>selenium-lambdatest-demo</artifactId>
   <version>1.0-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>selenium-lambdatest-demo</name>
   <url>http://maven.apache.org</url>

   <properties>
       <selenium-java.version>4.12.1</selenium-java.version>
       <testng.version>7.8.0</testng.version>
       <maven.compiler.version>3.11.0</maven.compiler.version>
       <surefire-version>3.1.2</surefire-version>
       <java.release.version>17</java.release.version>
       <maven.source.encoding>UTF-8</maven.source.encoding>
       <suite-xml>testng.xml</suite-xml>
       <argLine>-Dfile.encoding=UTF-8 -Xdebug -Xnoagent</argLine>
   </properties>

   <dependencies>
       <dependency>
           <groupId>org.seleniumhq.selenium</groupId>
           <artifactId>selenium-java</artifactId>
           <version>${selenium-java.version}</version>
       </dependency>
       <dependency>
           <groupId>org.testng</groupId>
           <artifactId>testng</artifactId>
           <version>${testng.version}</version>
           <scope>test</scope>
       </dependency>
   </dependencies>
   <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>${maven.compiler.version}</version>
               <configuration>
                   <release>${java.release.version}</release>
                   <encoding>${maven.source.encoding}</encoding>
                   <forceJavacCompilerUse>true</forceJavacCompilerUse>
               </configuration>
           </plugin>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-surefire-plugin</artifactId>
               <version>${surefire-version}</version>
               <executions>
                   <execution>
                       <goals>
                           <goal>test</goal>
                       </goals>
                   </execution>
               </executions>
               <configuration>
                   <useSystemClassLoader>false</useSystemClassLoader>
                   <properties>
                       <property>
                           <name>usedefaultlisteners</name>
                           <value>false</value>
                       </property>
                   </properties>
                   <suiteXmlFiles>
                       <suiteXmlFile>${suite-xml}</suiteXmlFile>
                   </suiteXmlFiles>
                   <argLine>${argLine}</argLine>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

With the dependencies updated in the pom.xml, the project setup is complete. We are good to start writing the web automation tests using Selenium WebDriver with Java

Writing the first test case using Selenium WebDriver and Java

We will use the Input form on LambdaTest's Selenium Playground to demo the workings of Selenium WebDriver with Java. The following is the Test Scenario we will be working on:

  1. Open LambdaTest’s Selenium Playground
  2. Click on the Input Form Demo link.
  3. Locate and enter values in all the fields on the Input Form Demo page.
  4. Click on the Submit button.
  5. Assert the message "Thanks for contacting us, we will get back to you shortly." displayed after the form is submitted successfully.

Image 1

LambdaTest’s Selenium Playground WebSite

Image 2

Input Form Demo page

Image 3

Success Message

Image 4

Setting up the configuration

As discussed in the earlier section on setting up the framework, we have already created the Maven project. The following screenshot of the project structure:

Image 5

We will run the tests on Chrome and Firefox browsers in the LambdaTest Cloud grid.

LambdaTest is an AI-powered test orchestration and execution platform that lets you perform test automation at scale on an online Selenium grid of 3000+ real desktop browsers, browser versions, and operating systems. It offers a robust, scalable, and highly secure cloud platform for test execution that empowers development and testing teams to expedite their release cycles. By enabling parallel testing, it significantly reduces test execution times by multiple folds.

Create a Java class and name the file as DriverManager. This class consists of the code that will help initiate the browsers along with setting up LambdaTest capabilities to run our test across different browsers, browser versions, and operating systems over the cloud.

File Name: DriverManager.java

Java
public class DriverManager {
   private static final ThreadLocal<WebDriver> DRIVER = new ThreadLocal<>();
   private static final String GRID_URL = "@hub.lambdatest.com/wd/hub";
   private static final String LT_ACCESS_TOKEN = System.getProperty("LT_ACCESS_KEY");
   private static final String LT_USERNAME = System.getProperty("LT_USERNAME");

   public void createDriver(final Browsers browser) {
       switch (browser) {
           case FIREFOX -> setupFirefoxDriver();
           case CHROME_CLOUD -> setupChromeInLambdaTest();
           case FIREFOX_CLOUD -> setupFirefoxInLambdaTest();
           default -> setupChromeDriver();
       }
       setupBrowserTimeouts();
   }

   public WebDriver getDriver() {
       return DriverManager.DRIVER.get();
   }

   public void quitDriver() {
       if (null != DRIVER.get()) {
           getDriver().quit();
           DRIVER.remove();
       }
   }

   private HashMap<String, Object> ltOptions() {
       final var ltOptions = new HashMap<String, Object>();
       ltOptions.put("username", LT_USERNAME);
       ltOptions.put("accessKey", LT_ACCESS_TOKEN);
       ltOptions.put("resolution", "2560x1440");
       ltOptions.put("selenium_version", "4.0.0");
       ltOptions.put("build", "LambdaTest Scale Demo");
       ltOptions.put("name", "LambdaTest tests at scale");
       ltOptions.put("acceptInsecureCerts", true);
       ltOptions.put("w3c", true);
       ltOptions.put("plugin", "java-testNG");
       return ltOptions;
   }

   private void setDriver(final WebDriver driver) {
       DriverManager.DRIVER.set(driver);
   }

   private void setupBrowserTimeouts() {
       getDriver().manage()
               .timeouts()
               .implicitlyWait(Duration.ofSeconds(20));
   }

   private void setupChromeDriver() {
       setDriver(new ChromeDriver());
   }

   private void setupChromeInLambdaTest() {
       final var browserOptions = new ChromeOptions();
       browserOptions.setPlatformName("Windows 10");
       browserOptions.setCapability("LT:Options", ltOptions());
       try {
           setDriver(
                   new RemoteWebDriver(new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_TOKEN, GRID_URL)),
                           browserOptions));
       } catch (final MalformedURLException e) {
           throw new Error("Error setting up cloud browser in LambdaTest", e);
       }
   }

   private void setupFirefoxInLambdaTest() {
       final var browserOptions = new FirefoxOptions();
       browserOptions.setPlatformName("Windows 10");
       browserOptions.setCapability("LT:Options", ltOptions());
       try {
           setDriver(
                   new RemoteWebDriver(new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_TOKEN, GRID_URL)),
                           browserOptions));
       } catch (final MalformedURLException e) {
           throw new Error("Error setting up cloud browser in LambdaTest", e);
       }
   }

   private void setupFirefoxDriver() {
       setDriver(new FirefoxDriver());
   }
}

Code Walkthrough

Before we jump to the outcome of this code, let us know to understand the code in a step-by-step process.

Step 1: Use the ThreadLocal class to instantiate the WebDriver instance. This ensures that each thread has its own isolated instance of the WebDriver.

Step 2: The primary advantage of ThreadLocal is ensuring a safe driver setting, especially when running tests in parallel. It isolates different threads, providing thread safety even if they set different values on the same ThreadLocal object.

Step 3: This isolation helps run threads independently and in isolation, preventing conflicts and issues.

When running tests on the LambdaTest platform, specific mandatory capabilities need to be set. You can use the LambdaTest Capabilities generator to configure these capabilities effectively. This ensures your tests are compatible and work seamlessly with the LambdaTest platform.

Capability Name Description
GRID_URL Remote URL required to run tests on LambdaTest Cloud grid
LT_USERNAME LambdaTest Username
LT_ACCESS_KEY LambdaTest Access Key

Image 6

LambdaTest Capabilities Generator allows setting the capabilities by selecting the needed configurations easily from the UI. Accordingly, it generates code that can be copy pasted easily into the project to start the browser automation test hassle-free.

Setting the capabilities in DriverManager class

The capabilities generated using the LambdaTest Capabilities generator have been updated in the ltOptions() method that will be used in the setupChromeInLambdaTest() and setupFirefoxInLambdaTest() methods for setting the capabilities on LambdaTest Cloud grid for running tests on Chrome and Firefox browsers respectively.

Image 7

setupChromeInLambdaTest() method

The additional parameter, like the Operating System, will be set using the setupChromeInLambdaTest() method. We will be using the "Windows 10" platform. The RemoteWebDriver() method of Selenium will help us in instantiating a new session of WebDriver on the LambdaTest Cloud that will be created by passing the GRID_URL, LT_USERNAME and LT_ACCESS_KEY.

Image 8

Similarly, the setupFirefoxInLambdaTest() method will help in creating a new session of Firefox browser on the "Windows 10" platform on the LambdaTest Cloud grid.

Image 9

On the LambdaTest Cloud grid based on the values passed in the testng.xml file for the browsers.

Image 10

The setupBrowserTimeouts() method will help set the implicit wait timeout for 20 seconds after successfully creating the browser session.

The implicit wait applies to all the web elements on the page and is added, so the driver should poll the page until an element is found or the timeout expires.

Image 11

Similarly, Explicit Wait can also be added for searching a web element where the driver is asked to wait for a certain condition invoked using the ExpectedConditions class.

With this, all the configurations are now set, and we can move on to write the automated tests for the test scenario we discussed earlier in the blog.

Configuring the test

Before we begin writing the tests, let's first create a BaseTest class that will hold the common configuration setup, like starting and quitting the browsers.

This BaseTest class will be extended by the actual Test class.

File Name: BaseTest.java

Java
public class BaseTest {
   protected DriverManager driverManager;

   @BeforeClass
   @Parameters("browser")
   public void setup(final String browser) {
       this.driverManager = new DriverManager();
       this.driverManager.createDriver(Browsers.valueOf(browser.toUpperCase()));
   }


   @AfterClass
   public void tearDown() {
       this.driverManager.quitDriver();
   }
}

The @Parameters annotation in TestNG has been used in this BaseTest class as it helps in the configuration of multiple parameters through testng.xml, thus removing the difficulty of updating the code repeatedly as the tests can be run on different browsers without any code modification. The browser values can be updated in the testng.xml, and the tests can be executed accordingly.

Implementation of test scenario

Just for a quick recap, we will be writing the tests for the following test scenario:

Image 12

The following test class, SeleniumDemoTests, has been created that will implement all the steps that we discussed in the test scenario.

File Name: SeleniumDemoTests.java

Java
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Parameters;

import java.sql.DriverManager;

public class SeleniumDemoTests extends BaseTest {

   @Test
   public void testInputForm() {
       this.driverManager.getDriver().navigate().to("https://www.lambdatest.com/selenium-playground/");

       final var mainPage = new MainPage(this.driverManager.getDriver());
       mainPage.clickOnLink("Input Form Submit");

       final var formDemoPage = new FormDemoPage(this.driverManager.getDriver());
       formDemoPage.fillForm("Faisal", "faisal@gmail.com", "Pass@111", "LambdaTest",
               "https://www.lambdatest.com", "India", "Mumbai",
               "Sector 22, Lane 1", "Landmark zone",
               "Maharashtra", "400001");
       assertEquals(formDemoPage.successMessage(), "Thanks for contacting us, we will get back to you shortly.");

   }
}

While writing tests, the Page Object Model is used, which involves creating two separate page object classes that require testing: Main Page and Input Form Demo page.

The MainPage class has the locators method for the LambdaTest’s Selenium Playground Main page. On this page, we need to locate the "Input Form Submit" link and click on it to open the Input Form Demo page.

File Name: MainPage.java

Java
public class MainPage {

   private final WebDriver driver;

   public MainPage(final WebDriver driver) {
       this.driver = driver;
   }

   public void clickOnLink(final String linkName) {
       this.driver.findElement(By.linkText(linkName)).click();
   }
}

To locate the "Input Form Submit" link, we will be using the Developer Tools option in the Chrome browser.

Image 13

As it is an href link, we can make use of the Selenium Selector LinkText to locate this element. To make the method more robust for locating all the links on this page, a generic method clickOnLink() has been created that accepts the link name in the string format and locates the link using the LinkText selector strategy and clicks on it. This way, we don't have to add duplicate lines of code to locate other links on this page.

Image 14

Next, let’s move to the "Form Demo" page that will be opened after the "Input Form Submit" link is clicked.

The FormDemoPage class has been created to house all the locators of the Form Demo page.

File Name: FormDemoPage.java

Java
public class FormDemoPage {
   private final WebDriver driver;
   private final WebDriverWait wait;

   public FormDemoPage(final WebDriver driver) {
       this.driver = driver;
       this.wait = new WebDriverWait(driver, Duration.ofSeconds(20));
   }

   private WebElement nameField() {
       return this.driver.findElement(By.id("name"));
   }

   private WebElement emailField() {
       return this.driver.findElement(By.id("inputEmail4"));
   }

   private WebElement passwordField() {
       return this.driver.findElement(By.id("inputPassword4"));
   }

   private WebElement companyField() {
       return this.driver.findElement(By.id("company"));
   }

   private WebElement websiteField() {
       return this.driver.findElement(By.id("websitename"));
   }

   private WebElement country() {
       return this.driver.findElement(By.name("country"));
   }

   private void countryName(final String countryName) {
       country().click();
       new Select(country()).selectByVisibleText(countryName);
   }

   private WebElement cityField() {
       return this.driver.findElement(By.id("inputCity"));
   }

   private WebElement addressLineOneField() {
       return this.driver.findElement(By.name("address_line1"));
   }

   private WebElement addressLineTwoField() {
       return this.driver.findElement(By.name("address_line2"));
   }

   private WebElement stateField() {
       return this.driver.findElement(By.id("inputState"));
   }

   private WebElement zipCodeField() {
       return this.driver.findElement(By.id("inputZip"));
   }

   private WebElement submitBtn() {
       return this.driver.findElement(By.cssSelector("#seleniumform button[type=\"submit\"]"));
   }

   public String successMessage() {
       return this.wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("p.success-msg"))).getText();
   }

   public void fillForm(final String name, final String email, final String password, final String company,
                        final String website, final String country, final String city, final String addressLineOne,
                        final String addressLineTwo, final String state, final String zipCode) {

       this.nameField().sendKeys(name);
       this.emailField().sendKeys(email);
       this.passwordField().sendKeys(password);
       this.companyField().sendKeys(company);
       this.websiteField().sendKeys(website);
       this.countryName(country);
       this.cityField().sendKeys(city);
       this.addressLineOneField().sendKeys(addressLineOne);
       this.addressLineTwoField().sendKeys(addressLineTwo);
       this.stateField().sendKeys(state);
       this.zipCodeField().sendKeys(zipCode);
       this.submitBtn().click();
   }
}

There are multiple text input fields on the Input Form Demo page like Name, Email, Password, Company, Website, City, State, etc. all these fields are located using the ID locator. For example, the below nameField() method returns the WebElement for the Name field. Likewise, separate methods are created for locating the other fields on this page.

Image 15

Country dropdown is a single dropdown field with the list of countries populated.

If we check the DOM of the Form Demo page, we can see that it has the Select tag on it.

Image 16

This clarifies that we can use the Select class of Selenium to interact with this field. The strategy to interact with this field is to first locate the Country field and then select the visible text of the Country Name from this field.

The country() method will locate and return the WebElement for the Country field. Next, the countryName() method will select the Country name provided in its parameter.

Image 17

The Submit button on the page needs to be located so we can submit the form.

Image 18

Image 19

Looking at the DOM of the page, it can be seen that there are no ID or name attributes for the Submit button. Here, we can make use of the CSS Selector locator strategy to locate the Submit button by using the selector - #seleniumform button[type = "submit"].

#seleniumform is the ID attribute for the Form on the Form Demo page, and within it, the button tag with the type attribute value "submit" is available for the Submit button.

Image 20

The fillForm() method will perform the interactions with all the fields on the Form Demo page and click on the Submit button. The test data will be provided as a parameter in this method in the actual test.

Image 21

Next, we will locate the success message text that will be displayed after the form is submitted successfully. Now, the catch here is the element will be visible after a few seconds of form submission. Hence, we will have to wait a few seconds before Selenium starts locating the element. If we don’t wait, we will get a NoSuchElementException for the success message text, as Selenium will directly start searching for the text after the form submission.

To handle this waiting, we will be calling the presenceOfElementLocatedBy() method from the expected_conditions class using Explicit Wait in Selenium.

The successMessage() method will wait for the WebElement to be present and, once it is available, will locate the WebElement and return its text in String format.

Image 22

The following is the test that we discussed in the earlier section of this blog that implements the test scenario.

Image 23

The test will first navigate to the LamdaTest’s Selenium playground website. Next, it will click on the "Input Form Submit" link on the main page. Once the Form Demo page is loaded, it will fill in the form using the fillForm() method from the FormDemoPage class.

The last statement of the test is for performing the assertion. It will check that the success message text is equal to "Thanks for contacting us, we will get back to you shortly."

Running automation tests at scale using Selenium on a LambdaTest cloud grid

The following testng.xml file will be used to run the test.

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Selenium WebDriver Demo Suite for LambdaTests" parallel="tests">
   <test name="Selenium Playground - Input Form Demo Test on Chrome">
       <parameter name="browser" value="chrome"/>
       <classes>
           <class name="io.github.mfaisalkhatri.SeleniumDemoTests">
               <methods>
                   <include name="testInputForm"/>
               </methods>
           </class>
       </classes>
   </test>
   <test name="Selenium Playground - Input Form Demo Test on Firefox">
       <parameter name="browser" value="firefox"/>
       <classes>
           <class name="io.github.mfaisalkhatri.SeleniumDemoTests">
               <methods>
                   <include name="testInputForm"/>
               </methods>
           </class>
       </classes>
   </test>
   <test name="Selenium Playground - Input Form Demo Test on LambdaTest Chrome">
       <parameter name="browser" value="chrome_cloud"/>
       <classes>
           <class name="io.github.mfaisalkhatri.SeleniumDemoTests">
               <methods>
                   <include name="testInputForm"/>
               </methods>
           </class>
       </classes>
   </test>
   <test name="Selenium Playground - Input Form Demo Test on LambdaTest Firefox">
       <parameter name="browser" value="firefox_cloud"/>
       <classes>
           <class name="io.github.mfaisalkhatri.SeleniumDemoTests">
               <methods>
                   <include name="testInputForm"/>
               </methods>
           </class>
       </classes>
   </test>
</suite>

GitHub

There are a total of 4 different test blocks in the testng.xml file. All the tests will be running in parallel. Parallel execution of the tests helps in saving the test execution time because all tests are performed concurrently.

We need to mention parallel="tests" for performing parallel testing in TestNG, in testng.xml. Here, all the test blocks updated in testng.xml will be executed in parallel.

The first and second test blocks will execute the test scenario on the local machine on Chrome and Firefox browsers, respectively. The third and fourth test blocks will execute the test scenario on the LambdaTest Cloud grid on Chrome and Firefox browsers respectively.

Our test execution is thread-safe as we have incorporated the ThreadLocal class into our code. Hence, there is no need to worry about the overlapping test session issues.

Screenshot of the test executed using IntelliJ IDE

Image 24

Tests executed on the LambdaTest Cloud grid can be viewed on the LambdaTest Dashboard. It provides a fair visibility of the test execution by providing step-by-step granular details of the test run in LambdaTest Analytics. Details like video recordings, device logs, screenshots, etc can also be viewed on it.

Check out the screenshots below, which will give you a fair insight into the dashboard for web automation tests.

LambdaTest Dashboard

Image 25

The details of the build and the tests that were conducted are visible in the following screenshots. Each test includes the test name, browser name, browser version, OS name, respective OS version, and screen resolution.

It also has the video of the test that was run, giving a better idea about how tests were run on the browsers.

Test run on Chrome browser on LambdaTest Cloud

Image 26

Test run on Firefox browser on LambdaTest Cloud

Image 27

Conclusion

Selenium WebDriver is a popular web automation framework that automates web browser-related tests. Test Automation helps in faster feedback and running all the regression and functional tests quickly.

By integrating Selenium with Cloud providers like LambdaTest, we can run the Web automation tests at scale on different browsers and versions without worrying about the Operating System and Browser installations, as all the infrastructure is provided on demand.

After the test execution is completed successfully, detailed insight into the tests can be viewed with all the granular details that can help report stuff to the software teams and the stakeholders.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Shahzeb works as a senior product marketing manager at LambdaTest. With more than 10+ years of diverse working experience, especially in the domain of Quality Engineering, Security, and E-Learning, he loves to share his thoughts about the latest trends and technologies in the industry.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA18-Oct-23 16:20
professionalȘtefan-Mihai MOGA18-Oct-23 16:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.