IntelliJ IDEA: Selenium WebDriver automated web tests with Page Objects in 15 minutes
By: Dmitry Yarygin
Tags: Software Development, Testing, Test Automation, Programming, Web Development

3. Pick any name for the project (e.g “Form Automation”) and press “Finish” to create this project

And after a few moments, we should have a template that we can work with. You might have a question about Maven

Maven is a build automation tool, used primarily for Java projects. We will be using it to add additional libraries to our project

Location of pom.xml file in our project structure

After we create our project and open pom.xml file it will look similar to this:

<?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>org.example</groupId>

<artifactId>Form Automation</artifactId>

<version>1.0-SNAPSHOT</version>

</project>

Now we can use this file to add additional libraries that we can use to automate our application.

Here are the lines (highlighted in bold) that we should add to our project, so we are able to automate our Web application:

<?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>org.nomadicdmitry</groupId>

<artifactId>Form Automation</artifactId>

<version>1.0-SNAPSHOT</version>

<dependencies>

<dependency>

<groupId>org.seleniumhq.selenium</groupId>

<artifactId>selenium-server</artifactId>

<version>3.141.59</version>

</dependency>

<dependency>

<groupId>org.testng</groupId>

<artifactId>testng</artifactId>

<version>7.1.0</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-surefire-plugin</artifactId>

<version>3.0.0-M4</version>

</dependency>

</dependencies>

</project>

Once we add those lines, we have almost all necessary libraries that we will use in our project. To be specific:

Selenium Webdriver: org.seleniumhq.selenium

TestNG (unit testing framework): org.testng

Maven SureFire (easy test plan execution): org.apache.maven.plugins

A couple more things to note:

Appearance of downloaded libraries in IDEA

After adding those libraries make sure to press the button on the right side to download those libraries (the one with the “M” symbol). You should see it starts downloading those libraries Make sure to check the latest versions of those libraries to avoid any problems further down the road. Here is a link to Maven repository for Selenium. You can easily search necessary library and add the proper version to your project

We are ready to start writing the code. However, let’s first draft it out and then proceed.

Drafting up the code

Typically, most of the WebDriver tutorials describe the sequence of commands that we need to execute. Let’s follow this pattern for a moment and outline what actions we are performing here for our test:

System.setProperty("webdriver.chrome.driver", "chromedriver"); WebDriver driver = new ChromeDriver(); get(

driver.findElement(By.id("first-name")).sendKeys("First Name");

driver.findElement(By.id("last-name")).sendKeys("Last Name");

driver.findElement(By.xpath("//a[contains(text(),'Submit')]")).click();

driver.close(); driver. https://formy-project.herokuapp.com/form );driver.(By.id("first-name")).sendKeys("First Name");driver.(By.id("last-name")).sendKeys("Last Name");driver.(By.xpath("//a[contains(text(),'Submit')]")).click();driver.();

That’s it! Seven lines of code to have a working solution for automating a web form. To clarify, the first line shows the location to a chromedriver (since we are executing this script on Chrome) and the second line creates an instance of WebDriver that we work with later on.

The next lines are easy to analyze since we just load the page and performing actions with locators. This code will work and perform the necessary actions (except the assertion part that we discuss later on).

However, what is the problem with this code? The problem is that it’s nowhere near how it would be used in the real environment and the reasons for that are:

It’s not reusable and brings repetitive code if we decide to use it in other places It’s hard to maintain (imaging changing locators for each line if we call those locators multiple times) Eventually it will be hard to read and understand once more tests are added

Let’s get back to our project and plan the project structure.

Creating the project structure

We are going to use a PageObject approach as a basis to design our tests. Essentially, in terms of Java Classes it means that we will create a class for each website page and outline locators and methods to perform actions to this class. Also, there would be one “parent” class that we will be inheriting our objects (pages) from.

Project structure of Automated test

Additionally, we would need a class that wires up all classes together and has the variables to store our configuration settings.

Here is how our project structure would look like:

PageObject TestPlan Utils WebForm

Let’s go ahead and create those four classes in our project. Go ahead and open “[project]\src\test\” and right click on “test” folder. Select “New -> Java Class” and create those classes accordingly.

Writing code for Test Classes

Let’s first write the code for the PageObject class. Here is how it will look like:

// All Test Pages are inheriting from this class



import org.openqa.selenium.WebDriver;

import org.openqa.selenium.support.PageFactory;



public class PageObject {

protected WebDriver driver;



public PageObject(WebDriver driver){

this.driver = driver;

PageFactory.initElements(driver, this);

}

}

What is this code for? Well, it simplifies unnecessary code that we would have duplicated in each of our pages otherwise. We are using a PageFactory approach here that extends the PageObject model. As the name suggests, PageFactory allows us to create test pages with the same mechanism behind it. This way, we don’t need to write WebDriver initialization code for each of our pages.

Next, we need to work on our Utils class. This is where we will add information about the parameters of our tests and “helper” methods to avoid a repeatable code.

// Configuration class for Form Automation project



public class Utils {

final static String BASE_URL = "https://formy-project.herokuapp.com/form";

final static String CHROME_DRIVER_LOCATION = "chromedriver";

}

It should be easy to understand what this code does. We are simply creating two variables for us to integrate those later:

BASE_URL: Defines a URL that we will work on

CHROME_DRIVER_LOCATION: Path to a chromedriver

Why do we need chromedriver? So, we already have a WebDriver, but that’s not enough for our tests to function. We also need to download a binary of chromedriver, so that our tests will work properly on a specific browser and platform.

Location of WebDriver in our project

In this case, it’s Chrome. Please download the binary from the official website for your platform and place it in the location that you have specified in CHROME_DRIVER_LOCATION. For simplicity, in this example, I just copied it in the root project directory.

Downloading a ChromeDriver from the official website

Important: Please make sure that your browser version and platform matches the version of the chromedriver. If those are mismatching then your tests will not work.

We are ready to work on our WebForm class which is going to have locators and methods for working with our specific page.

// Page URL: https://formy-project.herokuapp.com/form import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;



public class WebForm extends PageObject{



private final String FIRST_NAME = "First Name";

private final String LAST_NAME = "Last Name";



@FindBy(id = "first-name")

private WebElement first_name;



@FindBy(id = "last-name")

private WebElement last_name;



@FindBy(xpath = "//a[contains(text(),'Submit')]")

private WebElement submit_button;



public WebForm(WebDriver driver) {

super(driver);

}



public void enterFirstName(){

this.first_name.sendKeys(FIRST_NAME);

}



public void enterLastName(){

this.last_name.sendKeys(LAST_NAME);

}



public void pressSubmitButton(){

this.submit_button.click();

}

}

Can you see the direction our code is going here? We are breaking up our code in multiple easily reusable blocks. The main benefit here is that our code becomes much more maintainable.

Now we have a PageObject skeleton, Utils class and locators and methods in WebForm class. The next step is to writing up the code to connect everything together. Let’s write our TestPlan class:

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.testng.annotations.AfterSuite;

import org.testng.annotations.BeforeSuite;

import org.testng.annotations.Test;



public class TestPlan {

private static final WebDriver driver = new ChromeDriver();



@BeforeSuite

public static void main(String[] args) {

// ChromeDriver location set up in Utils class

System.setProperty("webdriver.chrome.driver", Utils.CHROME_DRIVER_LOCATION);

}



@Test(testName = "Submit a WebForm")

public static void submitForm(){

driver.get(Utils.BASE_URL);

WebForm webForm = new WebForm(driver);

webForm.enterFirstName();

webForm.enterLastName();

webForm.pressSubmitButton();

}



@AfterSuite

public static void cleanUp(){

driver.manage().deleteAllCookies();

driver.close();

}

}

Could you see how our code works now? Here is what we perform here:

We are creating a new ChromeDriver instance to use in multiple methods We are setting up the chromedriver property to set up the chromedriver location We are using BeforeSuite, AfterSuite, and Test annotations to structure our tests. Those are annotations of the TestNG framework and allow us to mark each test as a set of executable blocks We are creating a new method to perform a code submission in ”submitForm()” method Afterward, we are loading a URL specified in Utils.BASE_URL which is our configuration class. If we decide to change a URL, we can just modify it there and it will be applied to our tests Next, we create an instance of WebForm class. This is where we perform methods with our WebForm as we outlined in the class itself. If needed, you can scroll up and see those methods described there. Now it looks like a defined set of instructions that is easy to modify if necessary After we are done with everything, we clear the cookies and close the instance of the driver. That’s it, our test is ready

The core of our test

Here comes the most interesting part: executing our test. Hover over the green “play” button on the left and press “Run Test”. This button allows executing individual tests marked with a “Test” annotation.

The Chrome browser should start and perform our test. After it finished, we should see a result at the bottom left:

Test Execution results from TestNG module

As we can see here it shows that those tests have passed and execution time on the right side. We have made our test code easier now, but we should also discuss a couple more things: Locators and Test Validation.

Web page Locators

We are using three locators in our application. Those are specified in WebForm class since it belongs to this page:

As we discussed earlier, we are using ID and Xpath types of locators here. ID is a more preferable way of identifying the elements on the HTML page, but IDs are not always assigned to objects and in those cases, we need to be more creative in terms of locating the element. Let’s talk about locating an element for a “First Name” field.

We should go to this URL in Chrome, right-click and select the “Inspect” option to see this dialog at the bottom.