Skip to content

eliasnogueira/selenium-java-lean-test-architecture

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Lean Test Automation Architecture using Java and Selenium WebDriver

Actions Status

This project delivers to you a complete lean test architecture for your web tests using the best frameworks and practices.

It has a complete solution to run tests in different ways:

  • local testing using the browser on your local machine
  • parallel (or single) testing using Selenium Docker
  • local testing using TestContainers
  • Distributed execution using Selenium Grid

Examples

Local testing execution example

Local testing execution example

Parallel testing execution example with Selenium Grid

Parallel testing execution example with Selenium Grid

Languages and Frameworks

This project uses the following languages and frameworks:

  • Java 25 as the programming language
  • TestNG as the UnitTest framework to support the test creation
  • Selenium WebDriver as the web browser automation framework using the Java binding
  • AssertJ as the fluent assertion library
  • Allure Report as the testing report strategy
  • DataFaker as the faker data generation strategy
  • Log4J2 as the logging management strategy
  • Owner to minimize the code to handle the properties file
  • TestContainers Webdriver Containers

Test architecture

We know that any automation project starts with a good test architecture.

This project can be your initial test architecture for a faster start. You will see the following items in this architecture:

Do you have any other items to add to this test architecture? Please do a pull request or open an issue to discuss.

Page Objects pattern

I will not explain the Page Object pattern because you can find a lot of good explanations and examples on the internet. Instead, I will explain what exactly about page objects I'm using in this project.

AbstractPageObject

This class has a protected constructor to remove the necessity to init the elements using the Page Factory. Also, it sets the timeout from the timeout property value located on general.properties file.

All the Page Object classes should extend the AbstractPageObject. It also tries to remove the driver object from the Page Object class as much as possible.

Important information

There's a NavigationPage on the common package inside the Page Objects. Notice that all the pages extend this one instead of the AbstractPageObject. I implemented this way:

  • because the previous and next buttons are fixed on the page (there's no refresh on the page)
  • to avoid creating or passing the new reference to the NavigationPage when we need to hit previous or next buttons

As much as possible avoid this strategy to not get an ElementNotFoundException or StaleElementReferenceException. Use this approach if you know that the page does not refresh.

Execution types

There are different execution types:

  • local
  • local-suite
  • selenium-grid
  • testcontainers

The TargetFactory class will resolve the target execution based on the target property value located on general.properties file. Its usage is placed on the BaseWeb class before each test execution.

Local execution

Local machine

This approach is automatically used when you run the test class in your IDE.

When the target is local the createLocalDriver() method is used from the BrowserFactory class to return the browser instance.

The browser used in the test is placed on the browser property in the general.properties file.

Local Suite

It's the same as the Local Execution, where the difference is that the browser is taken from the TestNG suite file instead of the general.properties file, enabling you to run multi-browser test approach locally.

Testcontainers

This execution type uses the WebDriver Containers in Testcontainers to run the tests in your machine, but using the Selenium docker images for Chrome or Firefox.

When the target is testcontainers the TargetFactory uses the createTestContainersInstance() method to initialize the container based on the browser set in the browser property. Currently, Testcontainers only supports Chrome and Firefox.

Example

mvn test -Pweb-execution -Dtarget=testcontainers -Dbrowser=chrome

Remote execution

Selenium Grid

The Selenium Grid approach executes the tests in remote machines (local or remote/cloud grid). When the target is selenium-grid the getOptions method is used from the BrowserFactory to return the browser option class as the remote execution needs the browser capability.

The DriverFactory class has an internal method createRemoteInstance to return a RemoteWebDriver instance based on the browser capability.

You must pay attention to the two required information regarding the remote execution: the grid.url and grid.port property values on the grid.properties file. You must update these values before the start.

If you are using the docker-compose.yml file to start the Docker Selenium grid, the values on the grid.properties file should work.

You can take a look at the Execution with Docker Selenium Distributed to run the parallel tests using this example.

BrowserFactory class

This Factory class is a Java enum that has all implemented browsers to use during the test execution. Each browser is an enum, and each enum implements four methods:

  • createLocalDriver(): creates the browser instance for the local execution. The browser driver is automatically managed by the WebDriverManager library
  • createDriver(): creates the browser instance for the remote execution
  • getOptions(): creates a new browser Options setting some specific configurations, and it's used for the remote executions using the Selenium Grid
  • createTestContainerDriver() : Creates selenium grid lightweight test container in Standalone mode with Chrome/Firefox/Edge browser support.

You can see that the createLocalDriver() method use the getOptions() to get specific browser configurations, as starting the browser maximized and others.

The getOptions() is also used for the remote execution as it is a subclass of the AbstractDriverOptions and can be automatically accepted as either a Capabilities or MutableCapabilities class, which is required by the RemoteWebDriver class.

DriverManager class

The class DriverManager create a ThreadLocal for the WebDriver instance, to make sure there's no conflict when we run it in parallel.

BaseTest

This testing pattern was implemented on the BaseWeb class to automatically run the pre (setup) and post (teardown) conditions.

The pre-condition uses @BeforeMethod from TestNG creates the browser instance based on the values passed either local or remote execution. The post-condition uses @AfterMethod to close the browser instance. Both have the alwaysRun parameter as true to force the run on a pipeline.

Pay attention that it was designed to open a browser instance to each @Test located in the test class.

This class also has the TestListener annotation which is a custom TestNG listener, and will be described in the next section.

TestListener

The TestListener is a class that implements ITestListener. The following method is used to help logging errors and attach additional information to the test report:

  • onTestStart: add the browser information to the test report
  • onTestFailure: log the exceptions and add a screenshot to the test report
  • onTestSkipped: add the skipped test to the log

Logging