Healenium: Self-Healing Library for Selenium-based Automated Tests

In this article, we're going to review a library called Healenium. It is an AI-powered open-source library for improving the stability of Selenium-based tests, handling changes of updated web and mobile elements automatically. We're going to mention the benefits of using AI in testing and a step-by-step tutorial on setting it up in your local environment.

What is Healenium?

Healenium is an open-source testing framework extension that improves the stability of Selenium-based test cases by handling updated web and mobile elements. Web and mobile applications are updated constantly every sprint, which may lead to a locator change. Healenium uses a type of machine-learning algorithm to analyze the current page state for changes, handle NoSuchElement test failures, and fix broken tests at runtime by replacing the failed locator with a new value that matches the best and performs an action with the new element successfully. After the test run, Healenium provides detailed reporting with the fixed locators and screenshots. In addition, it supports advanced features like a parallel test run, remote test run, iFrames, actions, selenide integration.

All of that helps to reduce the effort to create reliable Selenium tests and reduces the number of test cases that failed due to non-product issues. In addition, it changes the focus on regressions instead of test maintenance.

How Does It Work?

Healenium consists of a client part that integrates with test automation frameworks. It includes Tree-comparing dependency. An algorithm self-healing capabilities that analyzes the current DOM state searches in the current state for the best subsequence and generates a new CSS locator. It also implements Selenium WebDriver and overrides the findElement method, and if it catches the NoSuchElement exception, it triggers the Tree-comparing algorithm and starts self-healing.

The back-end part of Healenium is a server that uses PostgreSQL database, which performs interactions between the client partTree-comparing, and the database that stores the old and new locator values as related information like DOM page, method name, class name, screenshots, etc.

There are also plugins for Maven and Gradle that generate reports with healing results and communicate with the back-end to get information about the healing elements.

Healenium also provides a plugin for IntelliJ IDEA IDE for updating the codebase with the new values of the locators.

Setup & Demo

We're going to use Java with Maven to set up a simple test project using the Page Object Patternand we'll integrate Healenium to test its functionality.

NOTE: Currently, Healenium supports only Java but will be ported to other languages in the future.

Pre-requisites

In the following example, we're going to use:

- JetBrains IntelliJ IDEA IDE (you can use Eclipse as well, but there's no Eclipse plugin for updating the codebase)

- Docker Desktop (feel free to use the Docker CLI only, we use it here because it's more user-friendly for beginners)

- Bootstrap 5's Checkout form example (scroll down to Download Examples or use your own example)

Starting the Back-end

1. To start the back-end, first, you need these two files (right-click and Save Link As...):

docker-compose.yaml

init.sql

Here is the project structure we're going to use in our example:

healenium directory structure

2. Start a CMD/Terminal in the healenium directory (make sure Docker is up and running) and type:

docker-compose up -d

3. Wait for docker to download the images and finish the setup. Verify you have healenium back-end and database containers running using the Docker Desktop UI (or type docker ps):

docker desktop healenium

Now we are ready to start writing the test. 

Dependencies and Page Object Setup

4. From the bootstrap-5.0.0-examples folder that you downloaded, copy the folders checkout and assets to the src/main/resources directory.

To use Selenium WebDriver, WebDriverManager and JUnit5 for our tests, we're going to use the following Maven artifacts:

org.junit.jupiter:junit-jupiter-api:5.8.0-M1

org.junit.jupiter:junit-jupiter-engine:5.8.0-M1

org.seleniumhq.selenium:selenium-java:3.141.59

io.github.bonigarcia:webdrivermanager:4.2.2

We're not going into details about how the Page Object Pattern works. You can read the article about it here. However, here's what we've got in the three classes:

LocalPageElements:

public class LocalPageElements {
private final WebDriver driver;
public LocalPageElements(WebDriver driver) {
this.driver = driver;
}
public WebElement firstName() {
return driver.findElement(By.id("firstName"));
}
public WebElement lastName() {
return driver.findElement(By.id("lastName"));
}
public WebElement username() {
return driver.findElement(By.id("username"));
}
public WebElement email() {
return driver.findElement(By.id("email"));
}
public WebElement address1() {
return driver.findElement(By.id("address"));
}
public WebElement address2() {
return driver.findElement(By.id("address2"));
}
public Select country() {
return new Select(driver.findElement(By.id("country")));
}
public Select state() {
return new Select(driver.findElement(By.id("state")));
}
public WebElement zip() {
return driver.findElement(By.id("zip"));
}
public WebElement cardName() {
return driver.findElement(By.id("cc-name"));
}
public WebElement cardNumber() {
return driver.findElement(By.id("cc-number"));
}
public WebElement cardExpiration() {
return driver.findElement(By.id("cc-expiration"));
}
public WebElement cardCVV() {
return driver.findElement(By.id("cc-cvv"));
}
public WebElement submitButton() {
return driver.findElement(By.xpath("//button[text()='Continue to checkout']"));
}
}

As you can see, all fields are searched by their ID and the button is searched by its exact text content match.

LocalPageAssertions:

public class LocalPageAssertions {
private final WebDriver browser;
public LocalPageAssertions(WebDriver browser) {
this.browser = browser;
}
protected LocalPageElements elements() {
return new LocalPageElements(browser);
}
public void formSent() {
Assertions.assertTrue(browser.getCurrentUrl().contains("paymentMethod=on"), "Form not sent");
}
}

The only assertion we have is to check if the form is sent. This is verified by asserting that the current URL contains 'paymentMethod=on' since after submitting the form, the query parameter is added as follows:

.../index.html?paymentMethod=on

If one of the required fields is empty, there will be validation errors, and the form won't be sent, so we can be sure that everything is appropriately validated.

LocalPage:

public class LocalPage {
private final WebDriver driver;
private final URL url = getClass().getClassLoader().getResource("checkout/index.html");
public LocalPage(WebDriver driver) {
this.driver = driver;
}
protected LocalPageElements elements() {
return new LocalPageElements(driver);
}
public LocalPageAssertions assertions() {
return new LocalPageAssertions(driver);
}
public void navigate() {
driver.navigate().to(url);
}
public void fillInfo(ClientInfo clientInfo) {
elements().firstName().sendKeys(clientInfo.getFirstName());
elements().lastName().sendKeys(clientInfo.getLastName());
elements().username().sendKeys(clientInfo.getUsername());
elements().email().sendKeys(clientInfo.getEmail());
elements().address1().sendKeys(clientInfo.getAddress1());
elements().address2().sendKeys(clientInfo.getAddress2());
elements().country().selectByIndex(clientInfo.getCountry());
elements().state().selectByIndex(clientInfo.getState());
elements().zip().sendKeys(clientInfo.getZip());
elements().cardName().sendKeys(clientInfo.getCardName());
elements().cardNumber().sendKeys(clientInfo.getCardNumber());
elements().cardExpiration().sendKeys(clientInfo.getCardExpiration());
elements().cardCVV().sendKeys(clientInfo.getCardCVV());
elements().submitButton().click();
}
}

We have the fillInfo method that fills all the fields with information and clicks the button on the page.

Writing the test

After we have the page object and the dependencies set up, we need to write the test. Here's how we do it with the vanilla FirefoxDriver:

public class HealeniumTests {
public WebDriver driver;
@BeforeAll
public static void classInit() {
WebDriverManager.firefoxdriver().setup();
}
@BeforeEach
public void testInit() {
driver = new FirefoxDriver();
}
@AfterEach
public void testCleanup() {
driver.quit();
}
@Test
public void assertFormSent_When_ValidInfoInput() {
var localPage = new LocalPage(driver);
localPage.navigate();
var clientInfo = new ClientInfo();
clientInfo.setFirstName("Anton");
clientInfo.setLastName("Angelov");
clientInfo.setUsername("aangelov");
clientInfo.setEmail("info@berlinspaceflowers.com");
clientInfo.setAddress1("1 Willi Brandt Avenue Tiergarten");
clientInfo.setAddress2("Lützowplatz 17");
clientInfo.setCountry(1);
clientInfo.setState(1);
clientInfo.setZip("10115");
clientInfo.setCardName("Anton Angelov");
clientInfo.setCardNumber("1234567890123456");
clientInfo.setCardExpiration("12/23");
clientInfo.setCardCVV("123");
localPage.fillInfo(clientInfo);
localPage.assertions().formSent();
}
}

Healenium In Tests

Integrating Healenium in your framework is easy as adding one line of code. All you have to do is wrap the WebDriver in the new SelfHealingDriver. But, before that, you have to import the Healenium Maven dependency:

com.epam.healenium:healenium-web:3.0.3-beta-8

After that, all you need to do is change this in the testInit method:

public void testInit() {
WebDriver delegate = new FirefoxDriver(); // declare delegate
driver = SelfHealingDriver.create(delegate); // create Self-healing driver
}

Self-healing Demo

Run the tests at least once with the correct locators. After that, we go to the index.html located in the src/main/resources/checkout folder and make the following changes (you can try other changes if you want):

  • id="address" to id="address1"
  • id="cc-name" to id="card-name"
  • id="cc-number" to id="card-number"
  • id="cc-expiration" to id="card-expiration"
  • id="cc-cvv" to id="card-cvv"
  • Continue to checkout to Checkout

Normally this would break the locators as they depend on exact matches. Using the self-healing driver, though, the test will pass as the locators will be healed at runtime.

healenium healed locators

Red boxes indicate that the locator has been fixed, and as you can see, the action has been properly executed.

Suppose you want to further configure Healenium for matching score cap or recovery tries. In that case, you have to create a file called healenium.properties and put it in the test resources directory at src/test/resources. This is the content of the file:

recovery-tries = 1
score-cap = 0.5
heal-enabled = true
serverHost = localhost
serverPort = 7878

recovery-tries is the number of times the algorithm will try to find a matching locator

score-cap is the minimum matching score required for the found locator to be accepted. 0.5 stands for 50%

heal-enabled is an option to toggle the healing on or off. The accepted values are true and false

serverHost is the URL of the Docker Container server that we created while setting up the back-end

serverPort is the port of the server mentioned above

Adding Healenium Maven Reporting Plugin

In the pom.xml of your project, you must first add the dependency to the repository:

<pluginRepositories>
<pluginRepository>
<id>bintray-healenium</id>
<url>http://dl.bintray.com/epam/healenium</url>
</pluginRepository>
</pluginRepositories>

To run the test automatically using the mvn test, we'll also add maven-surefire-plugin. Here's the code to add both the Maven Surefire Plugin and the Healenium Report Maven plugin:

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
<plugin>
<groupId>com.epam.healenium</groupId>
<artifactId>hlm-report-mvn</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>hlmReport</id>
<phase>compile</phase>
<goals>
<goal>initReport</goal>
</goals>
</execution>
<execution>
<id>hlmReportB</id>
<phase>test</phase>
<goals>
<goal>buildReport</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

To generate a new report, run the tests through the command line using mvn clean test. After a successful test run, you'll see a link to the report generated in the console: 

console healenium report

After you open the link in a browser, you'll see a list of all the locators that have been fixed with their old and new values, as well as screenshots of the page on the places the locators have been fixed. There's a switch on the right side where you can check if the locator has been successfully resolved with the correct one or not.

healenium fixed locators

After you open the link in a browser, you'll see a list of all the locators that have been fixed with their old and new values, as well as screenshots of the page on the places the locators have been fixed. There's a switch on the right side where you can check if the locator has been successfully fixed with the correct one or not.

You can further integrate it into the IntelliJ IDEA IDE by downloading the IDE plugin called Healenium. It would be best to go to the locators you want to fix, right-click and choose Healing Results. You'll get a small window showing you a list of fixed locators you can choose from, as well as their matching score:

healed locator plugin intellij

Summary

Healenium is very easy to set up and use in any Java Selenium-based project. However, Selenium tests are often difficult to maintain due to instability. Self-healing test automation with Healenium fixes the traditional problems that teams face when adopting Selenium.

Self-healing capabilities allow to replace broken locators with their new value and fix the tests at runtime so UI changes will not affect automated E2E tests stability. This helps teams focus on writing new tests instead of maintaining and fixing old tests that broke due to non-product issues.

Online Training

  • JAVA

  • C#

  • NON-FUNCTIONAL

START:8 November 2021

Web Test Automation Fundamentals

LEVEL: 1

  • Java Level 1
  • Java Unit Testing Fundamentals
  • Source Control Introduction
  • Selenium WebDriver- Getting Started
  • Setup Continuous Integration Job
Duration: 20 hours

4 hour per day

-50% coupon code:

START:24 November 2021

Test Automation Advanced

LEVEL: 2

  • Java Level 2
  • WebDriver Level 2
  • Appium Level 1
  • WinAppDriver Level 1
  • WebDriver in Docker and Cloud
  • Test Reporting Solutions and Frameworks
  • Behavior-Driven Development
Duration: 30 hours

4 hour per day

-20% coupon code:

START:8 December 2021

Enterprise Test Automation Framework

LEVEL: 3 (Master Class)

After discussing the core characteristics, we will start writing the core feature piece by piece.
We will continuously elaborate on why we design the code the way it is and look into different designs and compare them. You will have exercises to finish a particular part or extend it further along with discussing design patterns and best practices in programming.

Duration: 30 hours

4 hour per day

-20% coupon code:

START: 13 September 2021

Web Test Automation Fundamentals

LEVEL: 1

  • C# Level 1
  • C# Unit Testing Fundamentals
  • Source Control Introduction
  • Selenium WebDriver- Getting Started
  • Setup Continuous Integration Job
Duration: 20 hours

4 hour per day

-50% coupon code:

START:29 September 2021

Test Automation Advanced

LEVEL: 2

  • C# Level 2
  • WebDriver Level 2
  • Appium Level 1
  • WinAppDriver Level 1
  • WebDriver in Docker and Cloud
  • Test Reporting Solutions and Frameworks
  • Behavior-Driven Development- SpecFlow
Duration: 30 hours

4 hour per day

-20% coupon code:

START:13 October 2021

Enterprise Test Automation Framework

LEVEL: 3 (Master Class)

After discussing the core characteristics, we will start writing the core feature piece by piece.
We will continuously elaborate on why we design the code the way it is and look into different designs and compare them. You will have exercises to finish a particular part or extend it further along with discussing design patterns and best practices in programming.

Duration: 30 hours

4 hour per day

-20% coupon code:

START: 13 September 2021

Performance Testing

  • Fundamentals of Performance Testing
  • Fundamentals of network technologie
  • Performance testing with WebPageTest
  • Performance test execution and automation
  • Introduction to Jmeter
  • Introduction to performance monitoring and tuning
  • Performance testing in the cloud
Duration: 24 hours

4 hour per day

-30% coupon code:

The post Healenium: Self-Healing Library for Selenium-based Automated Tests appeared first on Automate The Planet.

Healenium: Self-Healing Library for Selenium-based Automated Tests