A few years ago, I developed a set of regression tests using Selenium 2.53.0 and TestNG 6.9.9. These scripts served their purpose well at the time. Recently, I needed to reuse these scripts for a project, but to my surprise, they no longer functioned as expected.
Upon investigation, I found that the project was outdated due to deprecated Selenium dependencies and API changes introduced in newer versions. To ensure compatibility and leverage the latest features of Selenium, I decided to migrate the project to Selenium 4. Below, I outline the steps I took during this migration process.
Steps for Conversion
1. Update Selenium Dependency in pom.xml
The first step was to replace the outdated Selenium 2 dependency with the latest Selenium 4 version in the Maven pom.xml
file. I updated it as follows:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.21.0</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.10.2</version>
<scope>test</scope>
</dependency>
- Why TestNG?
Since my original project was built with TestNG for test management, I also updated the TestNG dependency to the latest version (7.10.2
).
After updating the pom.xml
file, I ran the following Maven command to ensure all dependencies were downloaded and updated:
mvn clean install
This step ensured that my project had the latest Selenium and TestNG libraries required for Selenium 4 compatibility.
2. Refactor the Code to Align with Selenium 4 Changes
Selenium 4 introduced several changes and improvements over Selenium 2. As part of the migration process, I reviewed and refactored the code to address these updates. Below are the key changes:
a. WebDriver Initialization
In Selenium 4, initializing WebDriver no longer requires manually setting the system property for the driver path. The updated approach simplifies this process, allowing you to directly instantiate the WebDriver.
- Before (Selenium 2):
WebDriver driver = new ChromeDriver(); System.setProperty("webdriver.chrome.driver", "//path//of//the//driver"); driver.get(baseUrl);
- After (Selenium 4):
WebDriver driver = new ChromeDriver(); driver.get(baseUrl);
b. Modernizing WebDriver Initialization
In Selenium 2, WebDriver initialization was simpler but less flexible. Selenium 4 introduced a more robust and modular way to configure WebDriver instances using Service
and Options
.
Before (Selenium 2):
WebDriver driver = new ChromeDriver();
After (Selenium 4):
import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.chrome.ChromeDriverService; ChromeDriverService service = new ChromeDriverService.Builder() .usingDriverExecutable(new File("path/to/chromedriver")) .build(); ChromeOptions options = new ChromeOptions(); options.addArguments("--start-maximized"); WebDriver driver = new ChromeDriver(service, options);
This approach is not only compliant with Selenium 4 but also enables more granular control over browser configurations.
c. Updating Deprecated Methods
In Selenium 2, methods like findElementByClassName
and findElementByCssSelector
were commonly used but are now deprecated in Selenium 4. I replaced these methods with the updated By
locators.
- Before (Selenium 2):
WebElement element = driver.findElementByClassName("example-class");
- After (Selenium 4):
WebElement element = driver.findElement(By.className("example-class"));
Similarly, other deprecated methods like findElementById
, findElementByXPath
, etc., were replaced using the By
class:
WebElement element = driver.findElement(By.id("example-id"));
WebElement element = driver.findElement(By.xpath("//div[@id='example']"));
d. Actions and Interactions
Selenium 4 enhanced the Actions API, making it more reliable for complex interactions. I refactored existing code using the updated Actions API:
Before (Selenium 2):
Actions actions = new Actions(driver); actions.moveToElement(element).perform();
After (Selenium 4):
Actions actions = new Actions(driver); actions.moveToElement(element).clickAndHold().build().perform();
e. Implicit and Explicit Waits
The WebDriverWait API was also improved in Selenium 4. I explored some of the new features.
Before (Selenium 2):
WebDriverWait wait = new WebDriverWait(driver, 10); WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.id("example-id")));
After (Selenium 4):
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.id("example-id")));
3. Implementing Additional Selenium 4 Features
After refactoring for compatibility, I explored some of the new features introduced in Selenium 4, such as:
Relative Locators:
Selenium 4 introduced relative locators, which simplify finding elements relative to other elements:WebElement element = driver.findElement(RelativeLocator.with(By.tagName("input")).above(anotherElement));
DevTools Protocol (CDP):
Selenium 4 allows direct interaction with Chrome DevTools Protocol for advanced debugging and network control:driver.executeCdpCommand("Network.enable", new HashMap<>());
4. Testing and Debugging
After making all the changes, I thoroughly tested the scripts to ensure they ran as expected. During this process, I:
- Fixed minor syntax errors caused by deprecated methods.
- Addressed any performance issues by adding efficient waits and optimizing browser configurations.
- Verified that all regression tests passed successfully in Selenium 4.
Final Outcome
By following the above steps, I successfully converted my Selenium 2 project to Selenium 4. The migration not only resolved the issues with running the scripts but also allowed me to leverage Selenium 4's new features for better stability, performance, and maintainability.