Write Concise Web Tests With Selenide and Testcontainers

Last Updated:  July 4, 2022 | Published: November 6, 2020

Good old web tests – extremely valuable, sometimes hard to maintain, and annoying once they get flaky. If you are familiar with Selenium, you might find your self-writing helper functions to e.g., wait on elements to be present in the DOM, or AJAX calls to finish. As Selenium is more or less a low-level API to manage the browser, this usually leads to boilerplate code for your projects. The Selenide project eliminates the shortcoming of the low-level API nature of Selenium. With Selenide, you can write concise, stable, and short web tests for your Java projects.

This article showcases Selenide 6 with Selenium 4 and Testcontainers using a Spring Boot Java 11 application with a Thymeleaf frontend.

Java Project Setup for Selenide

For a Maven-based project, we have to include the following dependency:

That's all we need. The Selenide dependency already includes Selenium's selenium-java dependency, so we don't have to include any Selenium-related dependency on our own. All specific browser APIs are part of this library, and we can start writing automated web tests for Chrome, Edge, Firefox, Opera, Safari, etc.

In case Spring Boot's dependency management selects an incompatible Selenium version, we can override the Selenium version with a property:

Apart from this, Selenide also ships with the WebDriverManager. This is a useful utility library to automate the management of browser drivers. As we always need the driver binary of our target browser (e.g. chromedriver) installed while executing the web tests, manually downloading them, and managing their versions is cumbersome.

The WebDriverManager project takes over this task for us. Prior to executing a web test, the WebDriverManager ensures to download the required driver binary or use an already existing one. Hence we don't have to fiddle around with setting any parameters like -Dwebdriver.chrome.driver.

At the end of this blog post, we'll also see how we can utilize the WebDriver module of Testcontainers so that the driver is running in isolation as part of a Docker container.

Accessing Browser Elements with Selenide

The first step of every web test is to open the browser for a specific URL. With Selenide, that's a one-liner:

For demonstration purposes, let's assume we'll use a Spring Boot application that exposes one Thymleaf view and a REST API endpoint. The view displays a table of books whenever someone clicks a button to fetch them via AJAX.

Once the browser window opens our application, we can access HTML elements. Selenide provides two functions to access any SelenideEelement (a wrapper around Selenium's WebElement): $ and $$ (good old jQuery memories).

Using $, we'll get the first element that matches your search, whereas with $$ we get a list of all the elements that match.

Selenide provides different mechanisms (similar to Selenium) to locate elements by …

  • id
  • tagName (e.g. h1)
  • xpath
  • className
  • cssSelector
  • etc.

In our example, we first want to make sure the book table does not exist inside the DOM. Next, we want to click the button to fetch data via the book REST API.  After that, we can verify that our book table is rendered.

With Selenide, that's a three-liner:

As soon as we have access to the SelenideElement, we can perform actions (e.g. click or fill inputs) or express expectations using a readable and fluent API.

In addition to this, we can make sure that our page only contains one h1 element:

Make Screenshots During the Web Test Execution

Most of the time, the execution of our web test is automated on a CI server (e.g., Jenkins or Gitlab CI). This makes it hard to actually see the automated test and understand the reason whenever the test fails.

Fortunately, Selenide creates a screenshot and captures the HTML for every failing test out-of-the-box. What's left to adjust is the location where Selenide stores these files. By default, it's build/reports. As a Maven fanboy, I want the location to be inside the target folder.

For those of you that use JUnit Jupiter, you can override this by registering the ScreenShooterExtension:

We can also configure this using the Configuration class of Selenide:

In addition to the screenshots on failure, we can also manually take screenshots throughout our test execution:

Use Testcontainers to Containerize the WebDriver

If you are familiar with Tescontainers, you might wonder if we can utilize its WebDriver module for the web tests with Selenide.

Good news: Yes, we can use Testcontainers to write web tests with Selenide for our Java projects!

For this to work, we need one additional dependency:

Even though Selenide ships with the WebDriverManager, we don't have to create the WebDriver with it. We can instruct Selenide to use a WebDriverthat we created with Testcontainers:

The full example for our existing Java web test with Selenide but now using Testcontainers to provide the WebDriver looks like the following:

Keep in mind that the driver is now running in an isolated Docker container, and we can't use localhost to refer to our application. That's why we expose the host port we want to access from within the BrowserWebDriverContainer. Exposing the host port with Testcontainers has to happen after we start our Tomcat server but before we start the Docker container.

For more hands-on testing examples with Selenide, consider enrolling for the Testing Spring Boot Applications Masterclass. We'll use Selenide to write web tests for the real-world Book Reviewr application (React, Spring Boot, AWS, PostgreSQL) and verify its happy-path user journeys.

… you'll even learn how to record the web tests with Selenide and Testcontainers as part of the Masterclass:

Testcontainers VNC Recording End-to-End test

The source code for the demo application and this introduction to Selenide is available on GitHub.

Have fun writing web tests with Selenide for your Java projects,

Philip

 

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
>