In this post, we will create a simple Maven project using Eclipse as IDE. Eclipse provides excellent support for creating Maven projects. We will take a step by step approach to create a TestNG project using Maven.
Install Maven in Eclipse:
Normally Eclipse comes with Maven plugin installed. We can confirm by clicking File -> New -> Project. In the new project wizard search for ‘Maven’. If Maven is already installed in IDE, then it will be listed as below.
If Maven is not available, then we have to go to Eclipse Marketplace to get the plugin. From the top Menu bar, click Help -> Eclipse Marketplace.
Click Eclipse Marketplace to open up the wizard. Type in ‘Maven’ and click Go, to search for Maven plugins. We have to select and install ‘Maven Integration for Eclipse’ plugin. In below case, since Maven is already installed, it will show as ‘installed’.
Maven Project Explanation:
After successful installation, our next step is to create a TestNG project in Eclipse using Maven.
Go to File -> New -> Other. In the wizard, type in ‘Maven’. If Maven is installed then it will be displayed.
Select Maven Project and click Next. We have the option to provide a new workspace location or else use the default one. We also have an option to create a simple project by skipping the archetype selection. For now, we will select our own archetype.
What is an archetype in Maven? Archetype is a plugin in Maven which will first ask to choose a type of archetype from the internal catalog and then generate a project template based on archetype selection. The type of archetype selection depends on the type of project that we need to build. For example if its a simple Java Project, then we will use ‘maven-archetype-quickstart’ archetype and if it’s a Java Web Application project then we will use ‘maven-archetype-webapp’ archetype. Thus in short Maven will create all the required folders, download all required classes and files based on archetype selection.
In this project, we will be selecting quick-start archetype. Click next to proceed.
Based on the selection of quick-start archetype, Maven will create a project structure inside our project.
Enter Group ID which is the package name, Artifact ID which is Project folder name. Click Finish to complete the project creation.
As we click Finish, Maven automatically creates the project structure and download all the necessary jars based on our archetype selection. As displayed below, inside project folder, there is a ‘src’ folder and which has ‘main’ and ‘test’ folders. ‘main’ folder will contain source code for the application and ‘test’ folder will contain source code for tests. Always remember Maven identifies a test file which has ‘Test’ added as a suffix. Like in below case, Maven by default creates two java files, one in ‘main’ folder and another in ‘test’ folder. The java file inside ‘test’ folder is named as ‘AppTest.java’.
As mentioned earlier, Maven not only creates project structures but also download all necessary jars for the archetype selected. If we expand ‘Maven Dependency’, we can see that Maven automatically downloads the JUnit jar and stores it in ‘.m2’ folder, which is on our local machine.
Now, what is ‘.m2’ folder? ‘.m2’ folder is created automatically when we create our first Maven project. This folder is called local repository and contains a settings.xml file that contains global settings for all maven executions and a folder called repository that holds all of the local copies of various maven artifacts, either caches of artifacts pulled down from remote repositories, such as Maven Central, or artifacts built by your local maven builds. The artifacts are organized in there in folder structures that mirror the groupId’s of the artifacts. In Windows OS, it will be placed in ‘C://Users/Username/.m2’ path and in Mac OS, it will be placed in ‘/Users/username/.m2’ path. In Mac OS, by default folder starting with ‘.’ are hidden. To display the hidden folders in Mac OC, type in the following command in Terminal and hit Enter.
-- Display hidden folders in Mac OS --
defaults write com.apple.Finder AppleShowAllFiles YES
settings.xml file is not required unless we want to change the default settings. We can create our own settings.xml file and place in our ‘.m2’ directory. To know more details on settings.xml file, visit Settings.xml page.
Next, comes the target directory. It is created by Maven and contains all the compiled classes, JAR files etc.
The most important part of a Maven project is pom.xml file. A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details used by Maven to build the project. It contains default values for most projects. Examples for this is the build directory, which is target; the source directory, which is src/main/java; the test source directory, which is src/test/java; and so on. All POMs extend the Super POM unless explicitly set. For more details on pom.xml file, visit pom.xml page.
Add TestNG class into Maven Project:
Our next task is to add a TestNG class and execute it as a Maven project.
For adding a TestNG class, we will need required jars as mentioned in our previous post Part 1: Scripting using TestNG…. However, in a Maven project, we don’t have to manually upload the jars. We will just update the pom.xml file to include the TestNG dependency.
To get a Maven dependency for any jar, we can directly Google it. Go to Google and type in ‘testng maven dependency’ and hit enter. Else we can directly go to mvn repository testng.
Copy the below code and paste it inside dependencies tag in pom.xml file. Note that we are using TestNG version 6.10.
Once we save the pom.xml file, Maven automatically downloads the required jars and stores them in our local repository. If we expand the Maven dependency, we can see the jar location.
To create a TestNG class, we will follow the same steps and reuse the same code mentioned in our previous post Part 1: Scripting using TestNG…. One point to remember is that the TestNG class is actually a test script, hence we have to place it in projectname/src/test/java folder. And also as mentioned before we have to add ‘Test‘ to the file name. Only then Maven will execute the test script.
Next step is to execute our TestNG script. Now there are many ways to execute the Maven Project. One way is to directly open the Command Prompt or Terminal and execute the below command. The commands are same for Windows OS and Mac OS.
<!-- Go to Project folder -->
cd /users/sejijohn/documents/workspace/TestMaven
<!-- First clean and the run test-->
mvn clean test
This maven command will first delete all the class files from the target folder, recompile all the class file and then run the test. The final result will be displayed in the Command Prompt or Terminal.
Another way is to execute using Eclipse. Right click Project Name and click Run As -> Maven Test.
Maven is based around the central concept of a build lifecycle. When we select Maven test, Maven actually executes all lifecycle phases above test in a sequential manner, which means it will first execute validate, followed by compile and then test. To know more Maven lifecycle, visit Maven lifecycle page.
With this, we completed our first Maven Project creation in Eclipse. Learning Maven is no easy task but it will help you in coming posts. Hope you were able to follow most of it. Till next post, Happy Learning!
Apache Maven is one important tool that we will be using in creating every single Selenium project from here. In short its a software project management and comprehension tool. It allows the developer to automate the process of creating the initial folder structure for Java application, performing the compilation, testing, packaging and deploying the final product. It is implemented in Java which makes it platform-independent. Java is also the best work environment for Maven. Some of the key feature of Maven are:
Convention over Configuration: Maven uses Convention over Configuration which means developers are not required to create build process themselves. Maven provides sensible default behavior for projects.
Dependency management: Maven encourages the use of a central repository of JARs and other dependencies. Maven comes with a mechanism to download any JARs required for building your project from a central JAR repository.
Repository: Project dependencies can be downloaded from local file system, from Internet or public repositories like Maven Central.
Extensible via plug-ins: The Maven build system is extensible via plug-ins, which allows to keep the Maven core small. The Maven core does for example not know how to compile Java source code, this is handled by the compiler plug-in.
We have to go through following topics like Maven Lifecycle, POM and Super POM, Standard Directory Layout, Repositories, Dependency mechanism, Plugins etc. to gain more knowledge on Maven. Complete Maven documentation is placed in Apache Maven Documentation .
Download and Install Maven
The first step is to download and install Maven in your machine. We need to follow below steps for installing Maven.
Open Browser and navigate to Google. Type in “Maven download” and go to downloads page in maven.apache website. Direct link to Maven download page Maven Download.
Click on the Binary zip archive link to start the download in the local machine. This is same for Windows and Mac machines.
Once download is complete in the local Downloads folder of our machine, we have to move the ‘apache-maven-3.5.0’ folder to a location which is consistent with other applications. For Mac machine, we can copy and paste the folder in your home folder, e.g. ‘/Users/sejijohn’ location. We can also use the Terminal in Mac to move the folder from Downloads to home folder. We can use the below command in Terminal to move the ‘apache-maven-3.5.0’ folder to Home. This command will require password authentication.
For Windows machine, we can paste the ‘apache-maven-3.5.0’ folder inside C Drive ‘Program Files/Apache’ folder.
Next step is to set the environment variables for Maven. For Mac OS, Open Terminal and type in the following command “vim .bash_profile” and then type ‘i’ to insert into the VIM Editor. We need to set M2_Home and MAVEN_HOME variables and then update existing PATH variable. Type in below commands in the VIM editor.
--Set up M2_HOME--
export M2_HOME=/Users/sejijohn/apache-maven-3.3.9
--Set up MAVEN_HOME--
export MAVEN_HOME=/Users/sejijohn/apache-maven-3.3.9
--Append existing PATH--
export PATH=$PATH:/Users/sejijohn/apache-maven-3.3.9/bin
To Save the entires in VIM Editor, hit Escape, type in: “:wq + Enter”.
To Quit without saving the editor, hit Escape, type in “:q!”. To save the entries in .bash_profile, use below command.
--Save the entries in .bash_profile--
source .bash_profile
For Windows OS, type “advanced system settings” in the search box (beside the Windows start button), click View advanced system settings. Select Advance tab and click Environment Variables button. To run maven from command line in Windows we have to set MAVEN_HOME, M2_HOME and PATH variables. In System variables, add a new ‘MAVEN_HOME’ variable and point it to the Maven folder inside C Drive and that is “C:\Program Files\Apache\apache-maven-3.5.0”. Add another variable ‘M2_HOME’ and point it to same Maven location in C Drive.
We need to update the PATH variable.For that we have to point it to bin folder inside Apache Maven folder.
Once the environment variables settings are completed, next step is to verify if the maven installation is successful. For that go to Mac Terminal or Windows Command Prompt and type in ‘mvn -version‘. If Maven installation is successful, we will see the below message.
With this we have completed the steps to install and configure Maven in Windows and Mac machines. In the next post we will create a sample Maven Project using Eclipse. Till then, Happy Learning!
TestNG default reports are not very attractive and they are very basic. In this post we will use an advanced HTML Report called Extent Report. We will use this reporting to create our own custom report. Below is a snapshot on how our custom report will look like.
To save time and effort we will be using already created Java Project ‘NewTourPageFactory‘ from our previous post Working with Page Factory…. We will add an additional class for our reporting and also update some existing classes. Below is the snapshot of the project structure of ‘NewTourPageFactory‘.
To start with Extent Reporting, first task is to download the Jar. Go to Google and type in “Extent Report jar download”. Click on the maven repository link to download the Jar or else you can go directly to Extent Report Jar download link to download the Jar. Here we will be using version 2.40.2.
Extent reports jar has a dependency with freemarker jar. So this too need to be downloaded. Here we are using ‘freemarker-2.3.23’ version.
Once both Jars are downloaded to the local folder, the next step is to add them into our Java Project. Right Click on the project name and click Properties. Click on Java Build Path -> Add External Jars. Go to the local folder where ‘extentreports-2.40.2’ and ‘freemarker-2.3.23’ are saved. Add Jars and click OK to close the window. Once complete, we will see them inside ‘Referenced Libraries‘.
The next step is to create one Extent Report Reports class. This class will contain all the required methods for starting the test, logging the test status and ending the test. Right click on ‘com.selenium.util‘ package and click New -> Class. Name the class file as ‘Reports‘.
Copy and paste the below code into the Reports class.
package com.selenium.util;
import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;
public class Reports {
public static ExtentReports extent;
public static ExtentTest test;
// Start Test Case
public void startTest(String testCaseName) {
/*Create ExtentReports object passing location and report name as argument. Notice a new Result Log folder will be created inside project and the report name will be TestReport.html*/
extent = new ExtentReports(System.getProperty("user.dir") + "/RESULT_LOG" + "/TestReport.html");
// Add details to our report
extent.addSystemInfo("Selenium Version", "3.0.1").addSystemInfo("Environment", "QA");
// Create ExtentTest passing test case name and description
test = extent.startTest(testCaseName, "Our First Test Report");
}
// Log Test status, Test name and Test details
public void logStatus(LogStatus testStatus, String testStepName, String testDetails) {
// Log test status
test.log(testStatus, testStepName, testDetails);
}
// Capture screenshot and log into report
public void screenshotLog(LogStatus logStatus, String testStepName, String screenShotPath) {
// Attach screenshots
test.log(logStatus, testStepName + test.addScreenCapture(screenShotPath));
}
// End Test Case
public void endTest() {
// End test
extent.endTest(test);
extent.flush();
extent.close();
}
}
Next steps is to add the logging information into our test steps. For that we will add few steps into our page object class. Here we will be updating ‘HomePage’ class inside ‘com.selenium.pages‘ package.
Replace the existing code of ‘HomePage’ class with below code.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import com.relevantcodes.extentreports.LogStatus;
import com.selenium.util.Log;
import com.selenium.util.Reports;
public class HomePage {
// Declare WebDriver
WebDriver driver;
Reports reports;
// Element Locators for username textbox
@FindBy(name = "userName")
private WebElement userName;
// Element Locators for password textbox
@FindBy(name = "password")
private WebElement password;
// Element Locators for login button
@FindBy(name = "login")
private WebElement login;
// Constructor. Driver Initialization
public HomePage(WebDriver driver) {
this.driver = driver;
reports = new Reports();
}
// Login method. Notice there is no return type
public void loginUser(String uname, String pwd) {
// Enter username
userName.sendKeys(uname);
// Log4j logging
Log.info("HomePage.loginUser - username entered");
// Extent Reprot Logging. Enter Log status for HTML report
reports.logStatus(LogStatus.PASS, "Enter user name", "User name entered" + " <span class='label success'>Success</span>");
// Enter password
password.sendKeys(pwd);
// Log4j logging
Log.info("HomePage.loginUser - password entered");
// Extent Reprot Logging. Enter Log status for HTML report
reports.logStatus(LogStatus.PASS, "Enter password", "Password entered" + " <span class='label success'>Success</span>");
// Click Login button
login.click();
// Log4j logging
Log.info("HomePage.loginUser - login button clicked");
// Extent Reprot Logging. Enter Log status for HTML report
reports.logStatus(LogStatus.PASS, "Click Login","Login button clicked" + " <span class='label success'> Success</span>");
}
}
In the above code, notice that we created an object of Reports class and used its methods for logging test status. We can log different test status like LogStatus.PASS, LogStatus.FAIL, LogStatus.ERROR etc based on our test.
We can take same approach for other page object classes. However in this post we will be updating only the ‘HomePage’ class.
Before logging the log status in extent report, we need to start the test. This will be done in the TestNG test case.
We will update the ‘BookFlight’ class inside ‘com.selenium.testcase‘ package with below code.
package com.selenium.testcase;
import org.testng.annotations.Test;
import com.selenium.pages.BookFlightPage;
import com.selenium.pages.FlightConfirmationPage;
import com.selenium.pages.FlightFinderPage;
import com.selenium.pages.HomePage;
import com.selenium.pages.SelectFlightPage;
import com.selenium.util.Log;
import com.selenium.util.Reports;
import org.testng.annotations.BeforeMethod;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.apache.log4j.xml.DOMConfigurator;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.PageFactory;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.BeforeTest;
public class BookFlight {
WebDriver driver;
HomePage homePage;
FlightFinderPage flightFinderPage;
SelectFlightPage selectFlightPage;
BookFlightPage bookFlightPage;
FlightConfirmationPage flightConfirmationPage;
Reports reports;
// Test Case
@Test(dataProvider = "newTourData")
public void bookFlight(String uname, String pwd, String departFrom, String departDate, String fname, String lname,
String ccNum) {
/*
* Test case logic.
*
*/
Log.startTestCase("Test Case bookFlight");
Log.info("BookFlight.bookFlight - Home Page Opens");
homePage.loginUser(uname, pwd);
Log.info("BookFlight.bookFlight - Flight Finder Page Opens");
flightFinderPage.findFlights(departFrom, departDate);
Log.info("BookFlight.bookFlight - Select Flight Page Opens");
selectFlightPage.reserveFlight();
Log.info("BookFlight.bookFlight - Book Flight Page Opens");
bookFlightPage.bookFlight(fname, lname, ccNum);
Log.info("BookFlight.bookFlight - Flight Confirmation Page Opens");
flightConfirmationPage.clickLogOut();
Log.endTestCase("Test Case bookFlight");
}
// Driver and Page Objects Initialization
@BeforeMethod
public void beforeMethod() {
// Initialize driver
driver = new ChromeDriver();
Log.info("BookFlight.beforeMethod - Chrome Driver Intialized");
// Page Factory Initialization for all page objects
homePage = PageFactory.initElements(driver, HomePage.class);
flightFinderPage = PageFactory.initElements(driver, FlightFinderPage.class);
selectFlightPage = PageFactory.initElements(driver, SelectFlightPage.class);
bookFlightPage = PageFactory.initElements(driver, BookFlightPage.class);
flightConfirmationPage = PageFactory.initElements(driver, FlightConfirmationPage.class);
Log.info("BookFlight.beforeMethod - Page Factory intialization complete");
// Start Extent Report
reports.startTest("Book Flight");
// Nvaigate to URL
driver.get("http://newtours.demoaut.com");
Log.info("BookFlight.beforeMethod - open New Tour URL");
// Maximize window
// driver.manage().window().maximize();
Log.info("BookFlight.beforeMethod - Browser maximized");
}
// Driver closure
@AfterMethod
public void afterMethod() {
// End Extent Report
reports.endTest();
// Close and quit the driver to close the Browser
driver.close();
driver.quit();
Log.info("BookFlight.afterMethod - Browser closed");
}
// Create test data
@DataProvider
public Object[][] newTourData() {
return new Object[][] { { "demo", "demo", "London", "7", "john", "Doe", "56465465" } };
}
// Setting System property
@BeforeTest
public void beforeTest() {
// Set System Property for driver location
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Set System Property for log file location
System.setProperty("logfoldername", sdf1.format(Calendar.getInstance().getTime()));
System.setProperty("logfilename", sdf2.format(Calendar.getInstance().getTime()) + "-LOG");
Log.info("BookFlight.beforeTest - System property setting complete");
DOMConfigurator.configure("src/log4j.xml");
Log.info("BookFlight.beforeTest - XML config complete");
// Create Extent Report object
reports = new Reports();
}
}
Notice that in the above code, we are creating a Reports object inside ‘@BeforeTest‘ method, starting the test inside ‘@BeforeMethod‘ and ending the test inside ‘@AfterMethod‘.
Final step is to execute our TestNG test case. Right click on ‘BookFlight’ class and click Run As -> TestNG Test.
After successful execution, a new folder named ‘RESULT_LOG’ will be created inside our project and the Extent Report will be placed in this folder. This is because, inside ‘startTest‘ method of Reports class, we are creating an Extent Report object by passing the location as argument.
/*Create ExtentReports object passing location and report name as
argument. Notice a new Result Log folder will be created inside
project and the report name will be TestReport.html*/
extent = new ExtentReports(System.getProperty("user.dir") + "/RESULT_LOG" + "/TestReport.html");
Right click on ‘TestReport.html’ to open on web browser. Below is the snapshots on how our test report will look like.
Notice that all the log status that we entered in ‘HomePage’ class are displayed in the HTML report.
Entent Reports help us in creating beautiful looking test reports. They are very easy to implement and also provide valuable information about our test. Hope this post is useful in creating some good looking reports. Many more to follow. Till then, Happy Learning!
Additional Information:
While logging the Extent Report status we have used some HTML code inside ‘HomePage’ class.
// Extent Reprot Logging. Enter Log status for HTML report
reports.logStatus(LogStatus.PASS, "Enter user name",
"User name entered" + " <span class='label success'>Success</span>");
The span class helps us in providing some meaningful logs and messages into our test report. Some more labels are also available for use.
In this post we will be focusing on some advanced topics in Selenium.
Selenium WebDriver Wait commands:
This is one of the most used commands. Most of the times the code executes much faster than the web page navigation. If not synchronized, then the test case execution will fail. To overcome this, we use different Wait commands to slow our execution. Each of these commands have their own purpose:
Implicit Wait: Implicit waits are used to provide a user specified waiting time between each consecutive test step/command across the entire test script. This means Implicit waits will be in place for the entire time the browser is open and is applicable to search for elements also. Once set, the implicit wait is set for the life of the WebDriver object instance. Below is the code for Implicit Wait.
// Initialize Chrome WebDriver
WebDriver driver = new ChromeDriver();
// Setting Implicit Wait for 10 seconds
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
// Navigate to Google Website
driver.get("http://www.google.com");
//Maximize browser window
driver.manage().window().maximize();
//Relative XPath for the text box
driver.findElement(By.xpath("//input[@id='lst-ib']")).sendKeys("selenium");
Explicit Wait: Explicit waits are used to stop the execution till the time a particular condition or Expected Condition is met or the maximum time has elapsed before throwing an “ElementNotVisibleException” exception. Explicit wait applies for specified element only. WebDriverWait by default calls the ExpectedCondition every 500 milliseconds until it returns successfully. Below is the code for Explicit Wait.
/ Initialize Chrome WebDriver
WebDriver driver = new ChromeDriver();
// Setting Implicit Wait for 10 seconds
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
// Navigate to Google Website
driver.get("http://www.google.com");
//Maximize browser window
driver.manage().window().maximize();
//Relative XPath for the text box
driver.findElement(By.xpath("//input[@id='lst-ib']")).sendKeys("selenium");
/* Setting Explicit Wait 10 seconds for Search icon button to be Clickable */
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement searchElement = wait.until(ExpectedConditions.elementToBeClickable(By.id("_fZl")));
//Once searchElement is clickable
searchElement.click();
Fluent Wait: Fluent wait is another type of Explicit wait and you can define polling and ignore the exception to continue with script execution in case element is not found. Using Fluent wait we can define the frequency with which Fluent Wait has to check the conditions defined, ignore specific types of exception waiting such as NoSuchElementExceptions while searching for an element and set maximum amount of time to wait for a condition.
/* Waiting 30 seconds for an element to be present on the page, checking */
// for its presence once every 5 seconds.
public void waitForElement(By by) {
new FluentWait(driver)
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class)
.until(new ExpectedCondition() {
@Override
public Boolean apply(WebDriver driver) {
WebElement element = driver.findElement(by);
if (element == null)
return false;
else {
System.out.println("WebElement found");
return true;
}
}
});
}
Page Load Timeout: Page Load Timeout sets the amount of time to wait for a page load to complete before throwing an error. If the timeout is negative, page loads can be indefinite.
Using Actions class we can emulate complex user gestures. Sometimes we need to perform a ‘mouse hover’ action on a web site or we need to perform chain of actions like going to menu, and then from there to sub menu etc.. Sometimes we may need to do a drag and drop on a web site. All of these actions are possible using Actions class.
Mouse Hover: Using Actions class we can do a mouse hover on any links in web page. Below is the code:
//Create actions object and pass WebDriver
Actions actions = new Actions(driver);
//Get the WebElement for Mouse Hover
WebElement mainMenu =driver.findElement(By.id("nav-menuitem-1-link"));
//Perform the mouse hover
//Use build().perform() to complete the action
action.moveToElement(mainMenu).build().perform();
Click Sub Menu inside Menu: Actions class can also be used to click sub menu inside main menu where the main menu appears only when we do a mouser hover.
//Create actions object and pass WebDriver
Actions actions = new Actions(driver);
//Get the WebElement for Main Menu
WebElement mainMenu =driver.findElement(By.id("nav-menuitem-1-link"));
//Move to Main Menu do mouse hover
action.moveToElement(mainMenu);
//Get the WebElement for Sub Menu
//Sub Menu will be displayed inside Menu
WebElement subMenu = driver.findElement(By.linkText("Best price search"));
//Use the actions object to move to sub menu and click
//Use build().perform() to complete the action
actions.moveToElement(subMenu).click().build().perform();
Drag and Drop: Using Actions class we can perform drag and drop a web element. This can be done either by providing the source and target elements or by providing source elements and x, y coordinates for the destination. Below is the sample code for performing drag and drop:
//Create actions object and pass WebDriver
Actions actions = new Actions(driver);
//Source Element
WebElement dragElement =driver.findElement(By.id("draggable"));
//Target Element
WebElement dropElement =driver.findElement(By.id("droppable"));
//Use the actions object to perform drag and drop using source target elements
actions.dragAndDrop(dragElement, dropElement).perform();
//Use the actions object to perform drag and drop using x, y coordinates
actions.dragAndDropBy(mainMenu, 300, 200).perform();
Double Click: Using Actions class we can perform double click on an element. Below sample code is for performing double click:
//Create actions object and pass WebDriver
Actions actions = new Actions(driver);
//Get the WebElement for Main Menu
WebElement mainMenu =driver.findElement(By.id("nav-menuitem-1-link"));
//Use the actions object to perform double click on Main Menu
actions.doubleClick(mainMenu);
Right Click: Using Actions class we can also perform right click on an element.
//Create actions object and pass WebDriver
Actions actions = new Actions(driver);
//Get the WebElement for Main Menu
WebElement mainMenu =driver.findElement(By.id("nav-menuitem-1-link"));
//Use contextClick method do right click on Main Menu
actions.contextClick(mainMenu);
Click, Hold and Move: This can be performed by click and hold a web element and then drag it to required location. Below is the set of code to perform the action:
/Create actions object and pass WebDriver
Actions actions = new Actions(driver);
//Source Element
WebElement dragElement =driver.findElement(By.id("draggable"));
//Target Element
WebElement dropElement =driver.findElement(By.id("droppable"));
/* Use actions object to click and hold the source element
and move to target element and release source element.
Use build().perform() to complete the action. */
actions.clickAndHold(mainMenu).moveToElement(subMenu).release(mainMenu).build().perform();
The same commands can be used for resizing the window. However we have to provide x, y coordinates of the resized window. In this case we have to select ‘moveByOffset’ method instead of ‘moveToElement’.
//Resize a window to x, y coordinates
actions.clickAndHold(mainMenu).moveByOffset(100, 100).release(mainMenu).build().perform();
Sliders: There are some web elements in a web page, which can be moved sideways or up and down like the volume or equalizer bars etc. These kind of movements can also be controlled using Actions class.
//Slide horizontally to forward direction. Note y coordinate is 0.
actions.clickAndHold(sliderElement).moveByOffset(100, 0).release(sliderElement).build().perform();
//Slide horizontally to backward direction. Note y coordinate is 0.
actions.clickAndHold(sliderElement).moveByOffset(-100, 0).release(sliderElement).build().perform();
Key Down/Up: keyDown performs a modifier key press, doesn’t release the modifier key. Subsequent interactions may assume it’s kept pressed. keyUp performs a key release. Below is the same code for performing keyDown and keyUp:
//Perform key press control a and then delete
action.keyDown(Keys.CONTROL).sendKeys("a").keyUp(Keys.CONTROL).sendKeys(Keys.DELETE).perform();
Handeling Frames:
Frames are nothing but HTML code embedded inside another HTML code. They are defined inside <iframe></iframe> tags in HTML. Using this tag we can identify if the inspected object is inside a frame. We can get the frames details using firebug add on in Firefox browser.
Sometimes in order to access the objects inside the frames, we have to switch the Web Driver to the frame where the web elements are located. Below is the sample code for accessing the frames in different ways:
/* Three ways of navigating the web driver to frame.
* 1. Using index number
* 2. Using String Name or ID
* 3. Using Web Element
*/
/* Switch to frame using index number.
* This is not reliable as the index number varies with different browsers */
driver.switchTo().frame(0);
//Access the web element inside the frame
driver.findElement(By.id("nav-menuitem-1-link")).click();
/* Switch to frame using ID or Name.
* Frame id = google_ads_iframe_/55875582/WMUS/homepage_0
*/
driver.switchTo().frame("google_ads_iframe_/55875582/WMUS/homepage_0");
//Access the web element inside the frame
driver.findElement(By.id("nav-menuitem-1-link")).click();
//Identify the frame element first
WebElement frameOne = driver.findElement(By.id("google_ads_iframe_/55875582/WMUS/homepage_0"));
//Pass the frame details to web driver
driver.switchTo().frame(frameOne);
//Access the web element inside the frame
driver.findElement(By.id("nav-menuitem-1-link")).click();
Once we are done with all the task in a particular frame we have to switch back to the main page. This is very important otherwise the Web Driver will always continue in current frame. Below is the sample code to switch back to main page.
//Switching back to main page
driver.switchTo().parentFrame();
Handeling Multiple Windows:
Sometimes when clicking on a web element, a new tab is opened in the browser. What if we want to move the focus of the Web Driver from the parent page to newly opened page? Using Selenium we can handle multiple windows.
The code works by first identifying the parent window ID and then by getting the Set (java.util package) of opened window IDs. Note that window IDs are unique thats why we are using Set. To switch to child window we will use the iterator method of Set. Below is the sample method to switch to child window and then return the driver.
public WebDriver getChildWindow(WebDriver driver) {
// getWindowHandle method will return the current id of the window
String parentWindow = driver.getWindowHandle();
// Get the set of ids of open windows
Set setWindow = driver.getWindowHandles();
// Create iterator object
Iterator getWindow = setWindow.iterator();
// Navigate to child window and return driver
while (getWindow.hasNext()) {
String childWindow = getWindow.next();
// If childWindow!=parentWindow
if (!childWindow.equalsIgnoreCase(parentWindow)) {
//Switch to child window
driver.switchTo().window(childWindow);
return driver;
}
}
return null;
}
To switch back to parent window:
//Switch to parent window
driver.switchTo().window(parentWindow);
Handeling Date Pickers:
Some web sites does not allow users to directly enter the date in text field. In those cases, the user need to select a date from the Calendar pop up. Below is one example of one such web site where the ‘Depart’ text box is non editable.
Although different web sites have their own implementation of date pickers, below sample code can be used in most cases.
// Setting System properties
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
// Initialize WebDriver
WebDriver driver = new ChromeDriver();
// Maximize browser window
driver.manage().window().maximize();
// Navigate to Test Website
driver.get("https://www.skyscanner.com/");
// Click on Date Field present in web page
driver.findElement(By.id("js-depart-input")).click();
// In Calendar click next icon to select next month
driver.findElement(By.xpath("//div[@class='calendar-info-bar datepicker_clearfix']/button[2]")).click();
// Select 05/16/2017 date from Calendar
driver.findElement(By.xpath("//table[@data-month='2017-05']/tbody/tr[3]/td[@data-id='2017-05-16']")).click();
// Verify the date in Date text box
String departDate = driver.findElement(By.id("js-depart-input")).getAttribute("data-date");
if (departDate.equals("2017-05-16")) {
System.out.println("PASS");
}
Scroll the Web Page:
For scrolling the web page up or down we can use the below code.
// Initialize WebDriver
WebDriver driver = new FirefoxDriver();
//Create JavascriptExecutor object
JavascriptExecutor jse = (JavascriptExecutor)driver;
//Page down by 400 px
jse.executeScript("scroll(0,400)");
//Scroll for a element
jse.executeScript("arguments[0].scrollIntoView(true);", webElement);
//Scroll Up using negative y coordinate
jse.executeScript("scroll(0,-400)");
Similarly scrolling sideways can be done by changing the x coordinates.
Take Screenshot of full page:
It is very important to take screenshots when we execute a test script. Taking a screenshot also help us to debug or analyze any failed cases. Below code can be used for taking a screenshot for the entire page. Notice that we need to import ‘org.apache.commons.io.FileUtils‘ and ‘import org.openqa.selenium.TakesScreenshot‘ packages. Also in the below code we are saving the screenshot image inside Project/IMAGE folder. The image file will be named as ‘fileName.jpg’.
//Take a screenshot
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(scrFile, new File(System.getProperty("user.dir")+"/IMAGES" + "/fileName" + ".jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Take Screenshot of Web Element:
Sometimes we don’t require a screenshot of an entire web page, instead what if we just need the screenshot of the object or the web element. This can be done by first taking the screenshot of the entire page and then cropping the web element using its coordinates. Below code can be used for taking a screenshot of a web element.
// Capture the Web Element
WebElement element = driver.findElement(By.name("elementName"));
//Get Height and Width of the Web Element
int eleWidth = element.getSize().getWidth();
int eleHeight = element.getSize().getHeight();
// Create a rectangle using Width, Height and element location
Rectangle rect = new Rectangle(eleWidth, eleHeight);
/* Used selenium Point class to get x y coordinates of Image element to
* get location(x y coordinates) of the element */
Point point = element.getLocation();
int xcord = point.getX();
int ycord = point.getY();
// Capture entire page screenshot as buffer.
File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
// Reading full image screenshot.
BufferedImage fullImg = ImageIO.read(scrFile);
//Cut Image using height, width and x y coordinates parameters.
BufferedImage dest = img.getSubimage(xcord, ycord, eleWidth, eleHeight);
ImageIO.write(dest, "png", scrFile);
//Used FileUtils class of apache.commons.io.
//Save Image screenshot in Project/Images folder
FileUtils.copyFile(scrFile, new File(System.getProperty("user.dir")+"/IMAGES"+ "/fileName" + ".png"));
In case if we are scrolling the web page, we need to make following changes.
//Page down by 400 px
((JavascriptExecutor)driver).executeScript("scroll(0,400)");
//Cut Image using height, width and x y coordinates parameters.
BufferedImage dest = img.getSubimage(xcord, ycord - 400, eleWidth, eleHeight);
ImageIO.write(dest, "png", scrFile);
With this we came to an end on some of the advanced topics in Selenium. Some of the topics are commonly used across all Selenium scripts. Many more interesting topics to follow. Till then, Happy Learning!
Sometimes when we run our test cases it is very important to share some information about our test, for example if the test is failed or passed, any specific message or information to user regarding any exceptions or errors etc. This not only provides additional information to the tester or user but also helps in debugging if the test case fails during execution.
The easiest way of logging information is to use ‘System.out.println‘, which prints information into the console. But this has many disadvantages. One of them is that the printed information is not saved anywhere, as a result it’s lost after every execution. Another main disadvantage of using ‘System.out.println‘ is that it has impact to the performance of our code. However we can overcome all these problems by using a Logging tool.
There are many logging tools or APIs available in market. Some of them are: Log4j, SLF4J, Logback etc.. However we will be concentrating on Log4j as it is very simple and easy to learn.
Log4j has built-in log levels that allow users to log information into variety of destinations like a database, file, console etc.. The log level can be configured in a XML or Properties file. Different log levels in decreasing order of severity:
OFF – The highest possible rank and is intended to turn off logging.
FATAL – Severe errors that cause premature termination.
ERROR – Other runtime errors or unexpected conditions.
WARN – Use of deprecated APIs, poor use of API, ‘almost’ errors, other runtime situations that are undesirable or unexpected.
INFO – Interesting runtime events (startup/shutdown).
DEBUG – Detailed information on the flow through the system.
First step is to download the Log4j jars. Go to Google and type in ‘Log4j Jar download’. Click on the first link and we will be taken to ‘logging.apache.org’ website. Click below link to navigate directly to the download page. Log4j Download.
Click ‘log4j-1.2.17.tar.gz’ as displayed below to navigate to download page.
Click the below link to start the download. Once clicked, ‘log4j-1.2.17.tar’ will be downloaded in ‘Downloads’ folder of your local machine.
Unzip ‘log4j-1.2.17.tar’ file to view the Log4j jar. We have to add this jar into our Java Project.
To add Log4j jar into our project, right click on the project name and click Properties. Click on Java Build Path -> Add External Jars. Go to the local folder where ‘log4j-1.2.17’ is saved. Add the Jar and click OK to close the window. Once complete, we will see ‘log4j-1.2.17’ jar inside ‘Referenced Libraries’.
Next steps is to add the Configuration file. Configuration file not only allow us to set the destination output where we want to print the log information but also help us in setting up the log levels. We can configure Log4j using both Property file as well as XML file. Here we will be focusing on XML file. Right click on ‘src‘ folder and click New -> Other. Select XML file as displayed below. Click Next and name the file as ‘log4j.xml’.
Copy and paste below code into ‘log4j.xml’ file.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<!-- Appender for printing in console -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<!-- %d{yyyy-MM-dd HH:mm:ss} refers to Date format 2017-03-23 15:54:44 INFO Log:15 -->
<!-- %-5p refers to Type of logging INFO-->
<!-- %c{1} -->
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
</layout>
</appender>
<!-- Appender for printing in file -->
<appender name="file" class="org.apache.log4j.RollingFileAppender">
<!-- Appending false -->
<param name="append" value="false" />
<!-- Maximum 10kb size. New file will be created and old file renamed -->
<param name="maxFileSize" value="10KB" />
<!-- Maximum 5 log files. Old ones will be deleted -->
<param name="maxBackupIndex" value="5" />
<!-- Location of log file -->
<param name="file" value="test-output/log/${logfoldername}/${logfilename}.log" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
</layout>
</appender><root>
<!-- Setting Logging level -->
<level value="info" />
<!-- Setting logging level for console -->
<appender-ref ref="console" />
<!-- Setting logging level for file -->
<appender-ref ref="file" />
</root>
</log4j:configuration>
In the above XML code, the appenders are defined first. We can have as many appenders based on different log destinations. In our case, we are naming them as ‘console’ and ‘file’ for printing logs to both console and file. Notice the class names used in both appenders. Inside appenders, a layout is defined, in our case ‘org.apache.log4j.PatternLayout’ class is used. This layout formats the logging event and return the result as String. The output depends on the conversion pattern. A conversion pattern is composed of literal text and format control expressions called conversion specifiers, which starts with a percent sign (%) and is followed by optional format modifiers and a conversion character. The log messages in our case will be printed in below format.
Notice that for file appender, we are setting configurable parameters like setting the file size, number of files, location for saving the file etc.. For setting the log file name with current date, we will be using system properties (‘logfoldername‘ and ‘logfilename‘) set in our TestNG script.
Next comes the root logger. The root logger mentioned in above XML code logs messages with level INFO or above to both destinations, console and file.
Next step is to create a Log class with log methods. We will use these log methods in all our project class. Right click on ‘src‘ folder and click New -> Package. Name the package as ‘com.selenium.util’ and click Finish to close the window. This is where we will place our Log class.
Right click on ‘com.selenium.util’ package and click New -> Class. Name the class as ‘Log’. Copy and paste the below code into our Log.class file. Notice that we are creating a logger object using which we will be printing different log messages.
package com.selenium.util;
import org.apache.log4j.Logger;
public class Log {
// Initialize Log4j logs
private static Logger Log = Logger.getLogger(Log.class.getName());//
// This is to print log for the beginning of the test case
public static void startTestCase(String sTestCaseName) {
Log.info("****************************************************************************************");
Log.info("$$$$$$$$$$$$$$$$$$$$$ " + sTestCaseName + " $$$$$$$$$$$$$$$$$$$$$$$$$");
Log.info("****************************************************************************************");
}
// This is to print log for the end of the test case
public static void endTestCase(String sTestCaseName) {
Log.info("XXXXXXXXXXXXXXXXXXXXXXX " + "-E---N---D-" + " XXXXXXXXXXXXXXXXXXXXXX");
}
// info method for printing info message
public static void info(String message) {
Log.info(message);
}
// warn method for printing warning message
public static void warn(String message) {
Log.warn(message);
}
// error method for printing error message
public static void error(String message) {
Log.error(message);
}
// fatal method for printing fatal message
public static void fatal(String message) {
Log.fatal(message);
}
// debug method for printing debug message
public static void debug(String message) {
Log.debug(message);
}
}
Next step is to update our Page Object and TestNG class with different log information. We will start with ‘HomePage’ class. Copy and paste below code to HomePage.java file. Notice that we have used ‘log.info’ methods to print log messages in console and file. Since we have set ‘level value=info‘ in ‘log4j.xml’ file, messages with priority equal or greater than ‘info‘ such as ‘warn‘, ‘error‘, ‘fatal‘ etc. will be printed. Other log level will not be printed.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import com.selenium.util.Log;
public class HomePage {
//Declare WebDriver
WebDriver driver;
//Element Locators for username textbox
@FindBy(name = "userName")
private WebElement userName;
//Element Locators for password textbox
@FindBy(name = "password")
private WebElement password;
//Element Locators for login button
@FindBy(name = "login")
private WebElement login;
//Constructor. Driver Initialization
public HomePage(WebDriver driver) {
this.driver = driver;
}
// Login method. Notice there is no return type
public void loginUser(String uname, String pwd) {
// Enter username
userName.sendKeys(uname);
Log.info("HomePage.loginUser - username entered");
// Enter password
password.sendKeys(pwd);
Log.info("HomePage.loginUser - password entered");
// Click Login button
login.click();
Log.info("HomePage.loginUser - login button clicked");
}
}
Update the ‘FlightFinderPage.java’ file with below code. Notice that we have used ‘log.info’ methods to print log messages in console and file.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
import com.selenium.util.Log;
public class FlightFinderPage {
//Declare WebDriver
WebDriver driver;
//Element Locators for one way radio button
@FindBy(xpath = "//input[@value='oneway']")
private WebElement onewayRadio;
//Select the location from dropdown
@FindBy(name = "fromPort")
private WebElement fromPortDrop;
//Select the day from dropdown
@FindBy(name = "fromDay")
private WebElement fromDayDrop;
//Click Business radio button
@FindBy(xpath = "//input[@value='Business']")
private WebElement businessRadio;
//Click find flights button
@FindBy(name = "findFlights")
private WebElement findFlightsButton;
//Constructor. Driver Initialization
public FlightFinderPage(WebDriver driver) {
this.driver = driver;
}
//Find Flights method. Notice there is no return type
public void findFlights(String departFrom, String departDate) {
// Click one way radio button
onewayRadio.click();
Log.info("FlightFinderPage.findFlights - One way Radio Button clicked");
// Select Departing From dropdown
Select dropFrom = new Select(fromPortDrop);
dropFrom.selectByValue(departFrom);
Log.info("FlightFinderPage.findFlights - Depart From Dropdown clicked");
// Select Departing Day dropdown
Select dropDay = new Select(fromDayDrop);
dropDay.selectByValue(departDate);
Log.info("FlightFinderPage.findFlights - Depart Date Dropdown clicked");
// Click business class
businessRadio.click();
Log.info("FlightFinderPage.findFlights - Business Radio Button clicked");
// Click Find Flights button
findFlightsButton.click();
Log.info("FlightFinderPage.findFlights - Find Flights Button clicked");
}
}
Update ‘SelectFlightPage.java’ file with below code.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import com.selenium.util.Log;
public class SelectFlightPage {
//Declare WebDriver
WebDriver driver;
//Element Locators for reserve flight button
//@FindBy(name = "reserveFlights")
@FindBy(how = How.NAME, using = "reserveFlights")
private WebElement reserveFlightsButton;
//Constructor. Driver Initialization
public SelectFlightPage(WebDriver driver) {
this.driver = driver;
}
// Reserve Flight method. Notice there is no return type
public void reserveFlight() {
// Click reserve Flights button
reserveFlightsButton.click();
Log.info("SelectFlightPage.reserveFlight - Reserve Flight button clicked");
}
}
Update ‘BookFlightPage.java’ file with below code. Notice that for better understanding we are including class.method name information in our log messages.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import com.selenium.util.Log;
public class BookFlightPage {
//Declare WebDriver
WebDriver driver;
//Element Locators for first name textbox
@FindBy(name = "passFirst0")
private WebElement firstNameTextBox;
//Element Locators for last name textbox
@FindBy(name = "passLast0")
private WebElement lastNameTextBox;
//Element Locators for credit number textbox
@FindBy(name = "creditnumber")
private WebElement ccNumberTextBox;
//Element Locators for Buy Flights button
@FindBy(name = "buyFlights")
private WebElement buyFlightsButton;
//Constructor. Driver Initialization
public BookFlightPage(WebDriver driver) {
this.driver = driver;
}
//Book Flight method.Notice there is no return type
public void bookFlight(String fname, String lname, String ccNumber) {
// Enter first name
firstNameTextBox.sendKeys(fname);
Log.info("BookFlightPage.bookFlight - First Name entered");
// Enter last name
lastNameTextBox.sendKeys(lname);
Log.info("BookFlightPage.bookFlight - Last Name entered");
// Enter credit card number
ccNumberTextBox.sendKeys(ccNumber);
Log.info("BookFlightPage.bookFlight - Credit card entered");
// Confirm purchase
buyFlightsButton.click();
Log.info("BookFlightPage.bookFlight - Buy Flights Button clicked");
}
}
Update ‘FlightConfirmationPage.java’ file with below code.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.testng.Assert;
import com.selenium.util.Log;
public class FlightConfirmationPage {
//Declare WebDriver
WebDriver driver;
//Element Locators for confirmation text
@FindBy(xpath = "//font[contains(text(),'Confirmation')]")
private WebElement confirmationText;
//Element Locators for logout button
@FindBy(xpath = "//img[@src='/images/forms/Logout.gif']")
private WebElement logOutButton;
//Constructor. Driver Initialization
public FlightConfirmationPage(WebDriver driver) {
this.driver = driver;
}
//Verify and Log out method.
public void clickLogOut() {
// Assert if Confirmation text is displayed
Assert.assertTrue(confirmationText.isDisplayed());
Log.info("FlightConfirmationPage.clickLogOut - Assertion completed");
// Click Logout button
logOutButton.click();
Log.info("FlightConfirmationPage.clickLogOut - Log Out Button clicked");
}
}
Final step is to update the TestNG script with log messages. Update ‘BookFlight.java’ file with below code. Notice inside ‘beforeTest’ method we are setting the location of ‘log4j.xml’ file and also setting system properties for ‘logfoldername‘ and ‘logfilename‘.
package com.selenium.testcase;
import org.testng.annotations.Test;
import com.selenium.pages.BookFlightPage;
import com.selenium.pages.FlightConfirmationPage;
import com.selenium.pages.FlightFinderPage;
import com.selenium.pages.HomePage;
import com.selenium.pages.SelectFlightPage;
import com.selenium.util.Log;
import org.testng.annotations.BeforeMethod;
import org.apache.log4j.xml.DOMConfigurator;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.PageFactory;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.BeforeTest;
public class BookFlight {
WebDriver driver;
HomePage homePage;
FlightFinderPage flightFinderPage;
SelectFlightPage selectFlightPage;
BookFlightPage bookFlightPage;
FlightConfirmationPage flightConfirmationPage;
// Test Case
@Test(dataProvider = "newTourData")
public void bookFlight(String uname, String pwd, String departFrom, String departDate, String fname, String lname,
String ccNum) {
/*
* Test case logic.
*
*/
Log.startTestCase("Test Case bookFlight");Log.info("BookFlight.bookFlight - Home Page Opens");
homePage.loginUser(uname, pwd);
Log.info("BookFlight.bookFlight - Flight Finder Page Opens");
flightFinderPage.findFlights(departFrom, departDate);
Log.info("BookFlight.bookFlight - Select Flight Page Opens");
selectFlightPage.reserveFlight();
Log.info("BookFlight.bookFlight - Book Flight Page Opens");
bookFlightPage.bookFlight(fname, lname, ccNum);
Log.info("BookFlight.bookFlight - Flight Confirmation Page Opens");
flightConfirmationPage.clickLogOut();
Log.endTestCase("Test Case bookFlight");
}
// Driver and Page Objects Initialization
@BeforeMethod
public void beforeMethod() {
// Initialize driver
driver = new ChromeDriver();
Log.info("BookFlight.beforeMethod - Chrome Driver Intialized");
// Maximize window
driver.manage().window().maximize();
Log.info("BookFlight.beforeMethod - Browser maximized");
// Page Factory Initialization for all page objects
homePage = PageFactory.initElements(driver, HomePage.class);
flightFinderPage = PageFactory.initElements(driver, FlightFinderPage.class);
selectFlightPage = PageFactory.initElements(driver, SelectFlightPage.class);
bookFlightPage = PageFactory.initElements(driver, BookFlightPage.class);
flightConfirmationPage = PageFactory.initElements(driver, FlightConfirmationPage.class);
Log.info("BookFlight.beforeMethod - Page Factory intialization complete");
// Nvaigate to URL
driver.get("http://newtours.demoaut.com");
Log.info("BookFlight.beforeMethod - open New Tour URL");
}
// Driver closure
@AfterMethod
public void afterMethod() {
// Close and quit the driver to close the Browser
driver.close();
driver.quit();
Log.info("BookFlight.afterMethod - Browser closed");
}
// Create test data
@DataProvider
public Object[][] newTourData() {
return new Object[][] { { "demo", "demo", "London", "7", "john", "Doe", "56465465" } };
}
// Setting System property
@BeforeTest
public void beforeTest() {
// Set System Property for driver location
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//Set System Property for log file location
System.setProperty("logfoldername", sdf1.format(Calendar.getInstance().getTime()));System.setProperty("logfilename", sdf2.format(Calendar.getInstance().getTime()) + "-LOG");Log.info("BookFlight.beforeTest - System property setting complete");
DOMConfigurator.configure("src/log4j.xml");
Log.info("BookFlight.beforeTest - XML config complete");
}
}
Below is how project structure looks like.
Now that we have completed all changes in our ‘NewTourPageFactory‘ project, next step is to run our TestNG script. Right click on ‘BookFlight’ class and click Run As -> TestNG Test.
After successful execution, log messages are printed in console as displayed below.
A new log file is created under ‘NewTourPageFactory/test-output/log’ folder. This is based on the setting ‘value=test-output/log/${logfoldername}/${logfilename}.log’ that we did in ‘log4j.xml’ file.
With this we came to an end of Log4j logging. We will be using the logging in future frameworks.
Till then Happy Learning!
Additional Information:
Instead of ‘log4j.xml’ file we can also use ‘log4j.properties’ file.
Delete the already created ‘log4j.xml’ file from ‘src‘ folder and comment the code ‘DOMConfigurator.configure(“src/log4j.xml”)’ inside ‘beforeTest’ method of ‘BookFlight.java’. Right click on ‘src‘ folder and click New -> Other. Select File under General folder.
Name the file as ‘log4j.properties’ and click finish to close the window. Copy and paste below details to the properties files.
# Root logger option
log4j.rootLogger=INFO, file, console
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.Append=False
log4j.appender.file.File=${user.dir}/test-output/log/${logfoldername}/${logfilename}.log
log4j.appender.file.MaxFileSize=10KB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# Direct log messages to console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
We can see the same results in console as well as in log file.
If we closely analyze the code in Part 1: Page Object Model…, we have not used Page Factory class anywhere in our code. Now, what is Page Factory? Page Factory is an inbuilt library inside Selenium WebDriver for supporting Page Object Model concept. One of the highlights of using Page Factory is that it reduces code duplication. Without Page Factory, we have to use the below code multiple times in the same Page Object.
// Enter username
driver.findElement(userName).sendKeys(uname);
Page Factory pattern works by first identifying web elements using ‘@FindBy’ annotation and then relying on the ‘PageFactory’ class to create an instance of the Page Object. It uses the ‘initElements’ method to initialize the Page Object, which has a constructor with ‘WebDriver’ as its sole argument. Below is the sample code of a Page Object using Page Factory pattern.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class SelectFlightPage {
//Declare WebDriver
WebDriver driver;
//Element Locators @FindBy annotation
@FindBy(name = "reserveFlights")
private WebElement reserveFlightsButton;
//Constructor for initializing the driver
public SelectFlightPage(WebDriver driver) {
this.driver = driver;
}
// Reserve Flight method
public void reserveFlight() {
// Click reserve Flights button
reserveFlightsButton.click();
}
}
We will automate same test case used in Part 1: Page Object Model… but this time using Page Factory pattern.
Test Case:
Navigate to http://newtours.demoaut.com website and login.
Select an itinerary and complete the booking.
Log out and close the browser.
We will create five Page Objects using Page Factory pattern for five different pages. They are:
Home Page or Login Page
Flight Finder Page
Select Flight Page
Book Flight Page
Flight Confirmation Page
New Project Creation:
Launch Eclipse. Create a new Java Project by clicking on File -> New -> Java Project. Name the Java Project as ‘NewTourPageFactory’. Click Next and then Finish to complete the project creation. Once the project is created, we will have ‘src‘ folder and ‘JRE System Library‘ by default.
Next step is to add the Selenium Jars. Right Click on the project name and click Properties. Click on Java Build Path -> Add External Jars. Go to the local folder where ‘selenium-server-standalone-3.0.1’ is saved. We will be using 3.0.1 version from our last project. Add the Jar and click OK to close the window. Once complete, we will see ‘Referenced Libraries‘ included in our project.
Next step is to add the Chrome Driver. Right click on the project name and click New -> Folder. Give the folder name as ‘Chrome’ and click Finish to close the window. Now place the Chrome driver that we already downloaded in our last project into the ‘Chrome’ folder.
Next step is to add the TestNG libraries into our project. Right click on the project ‘FrameworkDesign’ and click Properties -> Java Build Path -> Add Library. In Add Library window, select TestNG and click on Next. Click on Finish to add the TestNG Library. Click OK to close the Properties window.
Right click on the ‘src’ folder and click New -> Package. Name the package as ‘com.selenium.pages’ and click Finish to close the window. This is where we will place our page specific code and their methods.
Page Object Model with Page Factory:
We will now create class files for each of the five pages.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘HomePage’. In this class we will place Home Page element locators and methods. Click Finish to close the window. Notice that we will not require static main method in any of our class files.
This is how our Home Page looks like. Notice that we have three elements that need to be located.
Copy the below code and paste into ‘HomePage’ class. Notice how the page specific Element locator code are written using ‘@FindBy‘ annotation. Unlike the methods in our last ‘NewTour’ Project, this time the methods don’t have any return types.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePage {
//Declare WebDriver
WebDriver driver;
//Element Locators for username textbox
@FindBy(name = "userName")
private WebElement userName;
//Element Locators for password textbox
@FindBy(name = "password")
private WebElement password;
//Element Locators for login button
@FindBy(name = "login")
private WebElement login;
//Constructor. Driver Initialization
public HomePage(WebDriver driver) {
this.driver = driver;
}
// Login method. Notice there is no return type
public void loginUser(String uname, String pwd) {
// Enter username
userName.sendKeys(uname);
// Enter password
password.sendKeys(pwd);
// Click Login button
login.click();
}
}
We will create a second class for our next page. Once the login is successful, we will be taken to Flight Finder Page. Flight Finder page has many elements. For now we will be concentrating on ‘One Way’, ‘Departing From’, ‘On Date’ and ‘Continue’ elements. Below is how our Flight Finder page looks like.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘FlightFinderPage’. In this class we will place Flight Finder Page element locators and methods. Click Finish to close the window. Once done, copy the below code and paste into ‘FlightFinderPage’ class. Notice that the layout is similar to ‘HomePage’ class. We have one method that takes two parameters and has a return type object of ‘SelectFlightPage’ class. Notice that the method has no return type.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.Select;
public class FlightFinderPage {
//Declare WebDriver
WebDriver driver;
//Element Locators for one way radio button
@FindBy(xpath = "//input[@value='oneway']")
private WebElement onewayRadio;
//Select the location from dropdown
@FindBy(name = "fromPort")
private WebElement fromPortDrop;
//Select the day from dropdown
@FindBy(name = "fromDay")
private WebElement fromDayDrop;
//Click Business radio button
@FindBy(xpath = "//input[@value='Business']")
private WebElement businessRadio;
//Click find flights button
@FindBy(name = "findFlights")
private WebElement findFlightsButton;
//Constructor. Driver Initialization
public FlightFinderPage(WebDriver driver) {
this.driver = driver;
}
//Find Flights method. Notice there is no return type
public void findFlights(String departFrom, String departDate) {
// Click one way radio button
onewayRadio.click();
// Select Departing From dropdown
Select dropFrom = new Select(fromPortDrop);
dropFrom.selectByValue(departFrom);
// Select Departing Day dropdown
Select dropDay = new Select(fromDayDrop);
dropDay.selectByValue(departDate);
// Click business class
businessRadio.click();
// Click Find Flights button
findFlightsButton.click();
}
}
From Flight Finder page we will be taken to Select Flight page. Next step is to create a class file for Select Flight page. We will go with the default selection of flights and click continue to go to next page. Below is how our Select Flight page looks like.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘SelectFlightPage’. In this class we will place Select Flight Page element locators and methods. Click Finish to close the window. Once done, copy the below code and paste into ‘SelectFlightPage’ class. Notice that we have only one method that takes no parameters and there is no return type.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class SelectFlightPage {
//Declare WebDriver
WebDriver driver;
//Element Locators for reserve flight button
@FindBy(name = "reserveFlights")
private WebElement reserveFlightsButton;
//Constructor. Driver Initialization
public SelectFlightPage(WebDriver driver) {
this.driver = driver;
}
// Reserve Flight method. Notice there is no return type
public void reserveFlight() {
// Click reserve Flights button
reserveFlightsButton.click();
}
}
From Select Flight page we will navigate to Book Flight page. We will create a class file for Book Flight page. Although Book Flight page has many elements but we will be concentrating on ‘First Name’, ‘Last Name’ and ‘Number’ elements. Below is how our Book Flight page looks like.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘BookFlightPage’. In this class we will place Book Flight Page element locators and methods. Click Finish to close the window. Once done, copy the below code and paste into ‘BookFlightPage’ class. Notice that we have only one method that take three parameters and has no return type.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class BookFlightPage {
//Declare WebDriver
WebDriver driver;
//Element Locators for first name textbox
@FindBy(name = "passFirst0")
private WebElement firstNameTextBox;
//Element Locators for last name textbox
@FindBy(name = "passLast0")
private WebElement lastNameTextBox;
//Element Locators for credit number textbox
@FindBy(name = "creditnumber")
private WebElement ccNumberTextBox;
//Element Locators for Buy Flights button
@FindBy(name = "buyFlights")
private WebElement buyFlightsButton;
//Constructor. Driver Initialization
public BookFlightPage(WebDriver driver) {
this.driver = driver;
}
//Book Flight method.Notice there is no return type
public void bookFlight(String fname, String lname, String ccNumber) {
// Enter first name
firstNameTextBox.sendKeys(fname);
// Enter last name
lastNameTextBox.sendKeys(lname);
// Enter credit card number
ccNumberTextBox.sendKeys(ccNumber);
// Confirm purchase
buyFlightsButton.click();
}
}
Finally we will create one more class file for Flight Confirmation page. We will verify the confirmation message by using Assert statement and then log out from the page. Below is how our Flight Confirmation page looks like.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘FlightConfirmationPage’. In this class we will place Flight Confirmation Page element locators and methods. Click Finish to close the window. Notice that we have only one method that takes no parameters and has no return type. Copy the below code and paste into ‘FlightConfirmationPage’ class.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.testng.Assert;
public class FlightConfirmationPage {
//Declare WebDriver
WebDriver driver;
//Element Locators for confirmation text
@FindBy(xpath = "//font[contains(text(),'Confirmation')]")
private WebElement confirmationText;
//Element Locators for logout button
@FindBy(xpath = "//img[@src='/images/forms/Logout.gif']")
private WebElement logOutButton;
//Constructor. Driver Initialization
public FlightConfirmationPage(WebDriver driver) {
this.driver = driver;
}
//Verify and Log out method.
public void clickLogOut() {
// Assert if Confirmation text is displayed
Assert.assertTrue(confirmationText.isDisplayed());
// Click Logout button
logOutButton.click();
}
}
This is how our Project structure looks like. We have successfully implemented Page Object Model using Page Factory pattern in our framework. We created five class files for five different pages and each class file have its own page specific code.
TestNG Script:
Next step is to create TestNG script which will use the Page Objects to complete the execution.
Inside ‘NewTourPageFactory’ project, right click ‘src’ folder and click New -> Package. Name the package as ‘com.selenium.testcase’ and click Finish to close the window. This is where we will place our test case logic.
Right click on ‘com.selenium.testcase’ package inside ‘src’ folder and select TestNG -> Create TestNG Class as shown below.
Name the TestNG class as ‘BookFlight’. Select the following annotations – ‘@BeforeMethod’, ‘@AfterMethod’, ‘@DataProvider’, ‘@BeforeTest’.
Copy the below code and paste into ‘BookFlight’ class. Notice that inside ‘@BeforeMethod’, ‘PageFactory’ class uses the ‘initElements’ method to initialize the Page Object and pass the driver information.
package com.selenium.testcase;
import org.testng.annotations.Test;
import com.selenium.pages.BookFlightPage;
import com.selenium.pages.FlightConfirmationPage;
import com.selenium.pages.FlightFinderPage;
import com.selenium.pages.HomePage;
import com.selenium.pages.SelectFlightPage;
import org.testng.annotations.BeforeMethod;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.PageFactory;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.BeforeTest;
public class BookFlight {
WebDriver driver;
HomePage homePage;
FlightFinderPage flightFinderPage;
SelectFlightPage selectFlightPage;
BookFlightPage bookFlightPage;
FlightConfirmationPage flightConfirmationPage;
// Test Case
@Test(dataProvider = "newTourData")
public void bookFlight(String uname, String pwd, String departFrom, String departDate, String fname, String lname,
String ccNum) {
/*
* Test case logic.
*
*/
homePage.loginUser(uname, pwd);
flightFinderPage.findFlights(departFrom, departDate);
selectFlightPage.reserveFlight();
bookFlightPage.bookFlight(fname, lname, ccNum);
flightConfirmationPage.clickLogOut();
}
// Driver and Page Objects Initialization
@BeforeMethod
public void beforeMethod() {
// Initialize driver
driver = new ChromeDriver();
// Maximize window
driver.manage().window().maximize();
// Page Factory Initialization for all page objects
homePage = PageFactory.initElements(driver, HomePage.class);
flightFinderPage = PageFactory.initElements(driver, FlightFinderPage.class);
selectFlightPage = PageFactory.initElements(driver, SelectFlightPage.class);
bookFlightPage = PageFactory.initElements(driver, BookFlightPage.class);
flightConfirmationPage = PageFactory.initElements(driver, FlightConfirmationPage.class);
// Nvaigate to URL
driver.get("http://newtours.demoaut.com");
}
// Driver closure
@AfterMethod
public void afterMethod() {
// Close and quit the driver to close the Browser
driver.close();
driver.quit();
}
// Create test data
@DataProvider
public Object[][] newTourData() {
return new Object[][] { { "demo", "demo", "London", "7", "john", "Doe", "56465465" } };
}
// Setting System property
@BeforeTest
public void beforeTest() {
// Set System Property
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
}
}
To run our TestNG script, right click on ‘BookFlight’ class and click Run As -> TestNG Test.
After successful execution, below result is displayed.
With this we came to an end on Page Factory pattern. It is very important that we use the Page Factory pattern in Page Object Model as it reduces code duplication to large extent.
Many more interesting topics to follow. Till then, Happy Learning!
Additional Information:
There is also another way of initializing the Page Objects using Page Factory. Instead of initializing all the Page Objects inside ‘@BeforeMethod’ of TestNG script, we can initialize the Page Object of next page inside a method of current Page. Below code is an example of ‘SelectFlightPage’ class. Notice that ‘reserveFlight()’ method is having a return type of ‘BookFlightPage’.
package com.selenium.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class SelectFlightPage {
//Declare WebDriver
WebDriver driver;
//Element Locators for reserve flight button
@FindBy(name = "reserveFlights")
private WebElement reserveFlightsButton;
//Constructor. Driver Initialization
public SelectFlightPage(WebDriver driver) {
this.driver = driver;
}
// Reserve Flight method. Notice there is no return type
public BookFlightPage reserveFlight() {
// Click reserve Flights button
reserveFlightsButton.click();
return PageFactory.initElements(driver, BookFlightPage.class);
}
}
There is also another way of using ‘@FindBy‘ annotation. For this we have to import ‘org.openqa.selenium.support.How’. This is to make code more readable.
@FindBy(how = How.NAME, using = "reserveFlights")
private WebElement reserveFlightsButton;
We concluded our last post Part 1: Page Object Model… by creating Page Objects for our test case. In this post we will focus on the TestNG script creation inside ‘NewTour’. This TestNG script will use the Page Objects to complete the execution.
Inside ‘NewTour’ project, right click ‘src’ folder and click New -> Package. Name the package as ‘com.selenium.testcase’ and click Finish to close the window. This is where we will place our test case logic.
Right click on ‘com.selenium.testcase’ package inside ‘src’ folder and select TestNG -> Create TestNG Class as shown below.
Name the TestNG class as ‘BookFlight’. Select the following annotations – ‘@BeforeMethod’, ‘@AfterMethod’, ‘@DataProvider’, ‘@BeforeTest’.
Copy the below code and paste into ‘BookFlight’ class.
package com.selenium.testcase;
import org.testng.annotations.Test;
import com.selenium.pages.HomePage;
import org.testng.annotations.BeforeMethod;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.BeforeTest;
public class BookFlight {
WebDriver driver;
HomePage homePage;
//Test Case
@Test(dataProvider = "newTourData")
public void bookFlight(String uname, String pwd, String departFrom, String departDate, String fname, String lname,
String ccNum) {
// Create an object of HomePage
homePage = new HomePage(driver);
/*
* Test case logic.
* Notice the return type of methods
* are objects to next page.
* */
homePage.loginUser(uname, pwd)
.findFlights(departFrom, departDate)
.reserveFlight()
.bookFlight(fname, lname,
ccNum);
}
//Driver Initialization
@BeforeMethod
public void beforeMethod() {
// Initialize driver
driver = new ChromeDriver();
// Maximize window
driver.manage().window().maximize();
// Nvaigate to URL
driver.get("http://newtours.demoaut.com");
}
//Driver closure
@AfterMethod
public void afterMethod() {
// Close and quit the driver to close the Browser
driver.close();
driver.quit();
}
//Create test data
@DataProvider
public Object[][] newTourData() {
return new Object[][] { { "demo", "demo", "London", "7", "john", "Doe", "56465465" } };
}
//Setting System property
@BeforeTest
public void beforeTest() {
// Set System Property
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
}
}
Here we are using ‘@BeforeTest’ method to set the system property, ‘@BeforeMethod’ to initialize the driver, ‘@Test’ method for test case logic, ‘@AfterMethod’ to close the driver and ‘@DataProvider’ for passing our test data. Notice that in ‘@Test’ method we are using Page Objects to access different web elements.
To run our TestNG script, right click on ‘BookFlight’ class and click Run As -> TestNG Test.
Below is how our project structure looks like.
We can also use ‘testng.xml’ to execute our TestNG script however in this post we will directly execute our ‘BookFlight’ class. For more details on ‘testng.xml’, refer Part 3: Scripting using TestNG….
After successful execution, below result is displayed.
With this we came to an end to our second part of POM. In the next post we will concentrate on Page Factory, which is another interesting topic and it’s use in POM. Till then, Happy Learning!
Page Object Model or POM is a design concept that is used to make the code more cleaner and easier to maintain. So far we have included all our test case logic and page elements in a single class file. As our test coverage increases, any change in page elements will result in the maintenance of all class files wherever the elements are used. This makes our code harder to maintain. Page Object Model design pattern overcome this problem by separating the test code and page specific code such as locators and reusable methods in different class files.
In this post we will create a new Java Project using TestNG. We will implement Page Object Model design pattern in our framework to complete one flight booking in New Tours web site.
Test Case:
Navigate to http://newtours.demoaut.com website and login.
Select an itinerary and complete the booking.
Log out and close the browser.
Even though there are only three steps in our test case but in actual we will navigate through five different pages to complete our test. They are:
Home Page or Login Page
Flight Finder Page
Select Flight Page
Book Flight Page
Flight Confirmation Page
New Project Creation:
We will create a new Java Project for this exercise. Let’s get started.
Launch Eclipse. Create a new Java Project by clicking on File -> New -> Java Project. Name the Java Project as ‘NewTour’. Click Next and then Finish to complete the project creation. Once the project is created, we will have ‘src‘ folder and ‘JRE System Library‘ by default.
Next step is to add the Selenium Jars. Right Click on the project name and click Properties. Click on Java Build Path -> Add External Jars. Go to the local folder where ‘selenium-server-standalone-3.0.1’ is saved. We will be using 3.0.1 version from our last project. Add the Jar and click OK to close the window. Once complete, we will see ‘Referenced Libraries‘ included in our project.
Next step is to add the Chrome Driver. Right click on the project name and click New -> Folder. Give the folder name as ‘Chrome’ and click Finish to close the window. Now place the Chrome driver that we already downloaded in our last project into the ‘Chrome’ folder.
Next step is to add the TestNG libraries into our project. Right click on the project ‘FrameworkDesign’ and click Properties -> Java Build Path -> Add Library. In Add Library window, select TestNG and click on Next. Click on Finish to add the TestNG Library. Click OK to close the Properties window.
Right click on the ‘src’ folder and click New -> Package. Name the package as ‘com.selenium.pages’ and click Finish to close the window. This is where we will place our page specific code and their methods.
Page Object Model:
We will now create class files for each of the five pages.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘HomePage’. In this class we will place Home Page element locators and methods. Click Finish to close the window. Notice that we will not require static main method in any of our class files.
This is how our Home Page looks like. Notice that we have three elements that need to be located.
Copy the below code and paste into ‘HomePage’ class. Notice how the page specific Element locator code and method are written. The method takes two parameters and has a return type object of ‘FlightFinderPage’.
package com.selenium.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class HomePage {
//Declare WebDriver
WebDriver driver;
//Element Locators
By userName = By.name("userName");
By password = By.name("password");
By loginButton = By.name("login");
//Constructor. Driver Initialization
public HomePage(WebDriver driver) {
this.driver = driver;
}
// Login method
public FlightFinderPage loginUser(String uname, String pwd) {
// Enter username
driver.findElement(userName).sendKeys(uname);
// Enter password
driver.findElement(password).sendKeys(pwd);
// Click Login button
driver.findElement(loginButton).click();
/*
* return type of this method is a new object to next page */
return new FlightFinderPage(driver);
}
}
We will create a second class for our next page. Once the login is successful, we will be taken to Flight Finder Page. Flight Finder page has many elements. For now we will be concentrating on ‘One Way’, ‘Departing From’, ‘On Date’ and ‘Continue’ elements. Below is how our Flight Finder page looks like.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘FlightFinderPage’. In this class we will place Flight Finder Page element locators and methods. Click Finish to close the window. Once done, copy the below code and paste into ‘FlightFinderPage’ class. Notice that the layout is similar to ‘HomePage’ class. We have one method that takes two parameters and has a return type object of ‘SelectFlightPage’ class.
package com.selenium.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.Select;
public class FlightFinderPage {
//Declare WebDriver
WebDriver driver;
//Element Locators
By onewayRadio = By.xpath("//input[@value='oneway']");
By fromPortDrop = By.name("fromPort");
By fromDayDrop = By.name("fromDay");
By businessRadio = By.xpath("//input[@value='Business']");
By findFlightsButton = By.name("findFlights");
//Constructor. Driver Initialization
public FlightFinderPage(WebDriver driver) {
this.driver = driver;
}
public SelectFlightPage findFlights(String departFrom, String departDate) {
// Click one way radio button
driver.findElement(onewayRadio).click();
// Select Departing From dropdown
Select dropFrom = new Select(driver.findElement(fromPortDrop));
dropFrom.selectByValue(departFrom);
// Select Departing Day dropdown
Select dropDay = new Select(driver.findElement(fromDayDrop));
dropDay.selectByValue(departDate);
// Click business class
driver.findElement(businessRadio).click();
// Click Find Flights button
driver.findElement(findFlightsButton).click();
/*
* return type of this method is a new object to next page
*/
return new SelectFlightPage(driver);
}
}
From Flight Finder page we will be taken to Select Flight page. Next step is to create a class file for Select Flight page. We will go with the default selection of flights and click continue to go to next page. Below is how our Select Flight page looks like.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘SelectFlightPage’. In this class we will place Select Flight Page element locators and methods. Click Finish to close the window. Once done, copy the below code and paste into ‘SelectFlightPage’ class. Notice that we have only one method that takes no parameters and has a return type object of ‘BookFlightPage’ class.
package com.selenium.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class SelectFlightPage {
//Declare WebDriver
WebDriver driver;
//Element Locators
By reserveFlightsButton = By.name("reserveFlights");
//Constructor. Driver Initialization
public SelectFlightPage(WebDriver driver) {
this.driver = driver;
}
// Reserve Flight method
public BookFlightPage reserveFlight() {
// Click reserve Flights button
driver.findElement(reserveFlightsButton).click();
/*
* return type of this method is a new object to next page
*/
return new BookFlightPage(driver);
}
}
From Select Flight page we will navigate to Book Flight page. We will create a class file for Book Flight page. Although Book Flight page has many elements but we will be concentrating on ‘First Name’, ‘Last Name’ and ‘Number’ elements. Below is how our Book Flight page looks like.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘BookFlightPage’. In this class we will place Book Flight Page element locators and methods. Click Finish to close the window. Once done, copy the below code and paste into ‘BookFlightPage’ class. Notice that we have only one method that take three parameters and has a return type object of ‘FlightConfirmationPage’.
package com.selenium.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class BookFlightPage {
//Declare WebDriver
WebDriver driver;
//Element Locators
By firstNameTextBox = By.name("passFirst0");
By lastNameTextBox = By.name("passLast0");
By ccNumberTextBox = By.name("creditnumber");
By buyFlightsButton = By.name("buyFlights");
//Constructor. Driver Initialization
public BookFlightPage(WebDriver driver) {
this.driver = driver;
}
public FlightConfirmationPage bookFlight(String fname, String lname, String ccNumber) {
// Enter first name
driver.findElement(firstNameTextBox).sendKeys(fname);
// Enter last name
driver.findElement(lastNameTextBox).sendKeys(lname);
// Enter credit card number
driver.findElement(ccNumberTextBox).sendKeys(ccNumber);
// Confirm purchase
driver.findElement(buyFlightsButton).click();
/*
* return type of this method is a new object to next page
*/
return new FlightConfirmationPage(driver);
}
}
Finally we will create one more class file for Flight Confirmation page. We will verify the confirmation message by using Assert statement and then log out from the page. Below is how our Flight Confirmation page looks like.
Right click on ‘com.selenium.pages’ package and click New -> Class. Name the class as ‘FlightConfirmationPage’. In this class we will place Flight Confirmation Page element locators and methods. Click Finish to close the window. Notice that we have only one method that takes no parameters and has no return type. This is because our test case ends here and we don’t have any other page to navigate. Copy the below code and paste into ‘FlightConfirmationPage’ class.
package com.selenium.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.testng.Assert;
public class FlightConfirmationPage {
//Declare WebDriver
WebDriver driver;
//Element Locators
By confirmationText = By.xpath("//font[contains(text(),'Confirmation')]");
By logOutButton = By.xpath("//img[@src='/images/forms/Logout.gif']");
//Constructor. Driver Initialization
public FlightConfirmationPage(WebDriver driver) {
this.driver = driver;
}
//Verify and Log out method
public void clickLogOut() {
// Assert if Confirmation text is displayed
Assert.assertTrue(driver.findElement(confirmationText).isDisplayed());
// Click Logout button
driver.findElement(logOutButton).click();
}
}
This is how our Project structure looks like. We have successfully implemented Page Object Model in our framework. We created five class files for five different pages and each class file have its own page specific code.
We will continue with test case creation using TestNG in our next post. Till then, Happy Learning!
A Test Suite is a collection of tests. In our last post Part 2: Scripting using TestNG…, if we check the TestNG results of our ‘FirstTestNScript’ script, we had no control over Test Suite or Test name. By default they are named as ‘Default suite’ and ‘Default test’.
However with the use of testing.xml, not only we can provide names to our test suites and tests but also we can configure our test run, set test dependency, include or exclude any test, method, class or package and set priority etc. In this post we will go through the testng.xml file creation and also some of the ways to configure our ‘FirstTestNScript’ script.
Right click on our Java Project ‘FrameworkDesign’ and click New -> Other. Below window appears and we need to select XML file inside XML folder. Click Next and name the file as ‘testng.xml‘. Click Finish to close the window.
Once after completing the above step, we can see that the ‘testing.xml’ file is added to our Java project. There will be two tabs: Design and Source. In the Source tab, copy the below code.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- Naming our Suite as OurFirstSuite -->
<suite name="OurFirstSuite" verbose="1" >
<!-- Naming our Test as Functional. We can have as many tests -->
< test name="Functional" >
<!-- Add TestNG class for execution -->
< classes >
<!-- Note class name include package name also -->
< class name="com.selenium.testcase.FirstTestNScript" />
</classes >
</test >
</suite >
If we go through the above code, we provided our suite name as ‘OurFirstSuite’ in the <suite> tag. This is followed by <test name=”Functional”> tag. We can have as many <test> tags but with different name. Inside the <test> tags, <classes> tags are placed. Notice that class name is a combination of package name and TestNG class.
To execute our TestNG script using ‘testing.xml’, right click on ‘testing.xml’ file and click Run As -> TestNG Suite.
After successful execution, below results are displayed. Test Suite is now named as ‘OurFirstSuite’ and test is named as ‘Functional’.
What if we want to run only ‘secondTestCase’ test from our ‘FirstTestNScript’ script? We can configure ‘testng.xml’ to select only required ‘@Test’ methods. In the Source tab, copy the below code.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- Naming our Suite as OurFirstSuite -->
<suite name="OurFirstSuite" verbose="1" >
<!-- Naming our Test as Functional. We can have as many tests -->
<test name="Functional" >
<!-- Add TestNG class for execution -->
<classes>
<!-- Note class name include package name also -->
<class name="com.selenium.testcase.FirstTestNScript">
<!-- Include selective tests -->
<methods>
<!-- Only Second Test Case will be executed -->
<include name="secondTestCase">
</include>
</methods>
</class>
</classes>
</test>
</suite>
In the above code we are configuring ‘testing.xml’ to consider only one ‘@Test’ method from our ‘FirstTestNScript’ script. For this we have to use <method> tag followed by <include> tag. However to exclude a particular ‘@Test’ method from our test, we can use <exclude> tag inside <method> tag. After successful execution, below results are displayed. Only the included ‘@Test’ method is executed.
We can configure ‘testing.xml’ to consider packages instead of classes. This comes in handy when we are doing regression testing. In this case, TestNG will look at all the classes in the package and will retain only classes that have TestNG annotations. In the Source tab, copy the below code and try.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- Naming our Suite as OurFirstSuite -->
<suite name="OurFirstSuite" verbose="1">
<!-- Naming our Test as Functional. We can have as many tests -->
<test name="Regression">
<!-- Execute based on packages -->
<packages>
<!-- Specify package names instead of class names -->
<package name="com.selenium.testcase">
</package>
</packages>
</test>
</suite>
After successful execution, below results are displayed. Notice that there is one more TestNG script named ‘SampleTest’ added to the package ‘com.selenium.testcase’. TestNG executed both files. If we closely analyze the test results, under ‘Regression’ test, there are two TestNG scripts – ‘FirstTestNScript’ and ‘SampleTest’. Inside ‘FirstTestNScript’, ‘secondTestCase’ (1 time) and ‘firstTestCase’ (2 times) are executed. And inside ‘SampleTest’, ‘firstTest’ (2 times) and ‘secondTest’ (2 times) are executed. This is due to Data Provider set up done in ‘FirstTestNScript’ and ‘SampleTest’ is different.
Using TestNG we can also execute scripts belonging to set of groups. Groups are specified in ‘testng.xml’ file and need to be mentioned inside annotations. However one important point to remember is that groups must be mentioned for all annotations for which ‘@Test’ is dependant. For example in our ‘FirstTestNScript’ script, ‘firstTestCase’ is depending on ‘@BeforeTest’ for setting system properties, ‘@BeforeMethod’ for initializing WebDriver and ‘@AfterMethod’ for closing the WebDriver. Hence groups must be mentioned in all these methods. We will make below changes in our ‘FirstTestNScript’ script.
package com.selenium.testcase;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.BeforeClass;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.AfterSuite;
public class FirstTestNScript {
WebDriver driver;
@Test(dataProvider = "loginDP", priority=2, groups = "login")
public void firstTestCase(String username, String password) {
System.out.println("This is our First Test Method");
// Enter username as demo
driver.findElement(By.name("userName")).sendKeys(username);
// Enter password as demo
driver.findElement(By.name("password")).sendKeys(password);
// Click Sign in button
driver.findElement(By.name("login")).click();
// Assert if Sign Off is displayed after logging in
Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
System.out.println("First Test Case Passed");
}
// Assign WebDriver
@BeforeMethod(groups = "login")
public void beforeMethod() {
System.out.println("This is Before Method");
// Assign WebDriver to ChromeDriver
driver = new ChromeDriver();
// Maximize window
driver.manage().window().maximize();
// Navigate to New Tours URL
driver.get("http://newtours.demoaut.com");
}
@AfterMethod(groups = "login")
public void afterMethod() {
System.out.println("This is After Method");
// Close and quit the driver to close the Browser
driver.close();
driver.quit();
}
// Setting System Properties
@BeforeTest(groups = "login")
public void beforeTest() {
System.out.println("This is Before Test");
// Setting System properties
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
}
}
And we will update the ‘testng.xml’ file with below code.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- Naming our Suite as OurFirstSuite -->
<suite name="OurFirstSuite" verbose="1">
<!-- Naming our Test as Functional. We can have as many tests -->
<test name="Functional">
<!-- Include required groups to execute -->
<groups>
<!-- group = login -->
<run>
<include name="login" />
</run>
</groups>
<!-- Include classes where groups are mentioned -->
<classes>
<class name="com.selenium.testcase.FirstTestNScript" />
</classes>
</test>
</suite>
After successful execution, below results are displayed. Notice that only ‘firstTestCase’ is executed inside ‘FirstTestNScript’ script as it placed under ‘login’ groups.
We have learned passing parameters using Data Provider. There is another way of passing parameters and that is through ‘testing.xml’. The parameter values can be set at both suite and test level in the testng XML file and based on scope, parameters can be used in the TestNG script. However any parameter value defined at the test level will override the value of a parameter, with same name, if defined at suite level. We will make following changes in our ‘firstTestCase’ code. Notice that we removed Data Provider from our ‘firstTestCase’ code and used ‘@Parameters’ instead. No other changes to ‘FirstTestNScript’ class.
@Test(groups = "login")
@Parameters({"username","password"})
public void firstTestCase(String username, String password) {
System.out.println("This is our First Test Method");
// Enter username as demo
driver.findElement(By.name("userName")).sendKeys(username);
// Enter password as demo
driver.findElement(By.name("password")).sendKeys(password);
// Click Sign in button
driver.findElement(By.name("login")).click();
// Assert if Sign Off is displayed after logging in
Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
System.out.println("First Test Case Passed");
}
If we add parameters at suite level, then they are accessible to all the methods. In below example parameters ‘username’ and ‘password’ are accessible to all methods in TestNG script using ‘@Parameters’ annotation.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- Naming our Suite as OurFirstSuite -->
<suite name="OurFirstSuite" verbose="1">
<!-- Adding parameters at suite level -->
<parameter name="username" value="demo"></parameter>
<parameter name="password" value="demo"></parameter>
<!-- Naming our Test as Functional. We can have as many tests -->
<test name="Functional">
<!-- Include required groups to execute -->
<groups>
<!-- group = login -->
<run>
<include name="login" />
</run>
</groups>
<!-- Include classes where groups are mentioned -->
<classes>
<class name="com.selenium.testcase.FirstTestNScript" />
</classes>
</test>
</suite>
If we add parameters at test level, then they are accessible to only those methods that are in scope of test. In below example parameters ‘username’ and ‘password’ are accessible to only those methods that are in scope of <test name=”Functional”>.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- Naming our Suite as OurFirstSuite -->
<suite name="OurFirstSuite" verbose="1">
<!-- Naming our Test as Functional. We can have as many tests -->
<test name="Functional">
<!-- Adding parameters at test level -->
<parameter name="username" value="demo"></parameter>
<parameter name="password" value="demo"></parameter>
<!-- Include required groups to execute -->
<groups>
<!-- group = login -->
<run>
<include name="login" />
</run>
</groups>
<!-- Include classes where groups are mentioned -->
<classes>
<class name="com.selenium.testcase.FirstTestNScript" />
</classes>
</test>
</suite>
TestNG also allow us to send Optional parameters. If we don’t mention parameters in ‘testing.xml’ then there is way to pass parameters to test method using ‘@Optional‘ annotation. In below example, TestNG uses the parameters passed from ‘@Optional‘ for the ‘firstTestCase’. This happened because TestNG was unable to find parameters named ‘username’ and ‘password’ in the ‘testing.xml’ file.
@Test(groups = "login")
@Parameters({"username","password"})
public void firstTestCase(@Optional("demo") String username, @Optional("demo") String password) {
System.out.println("This is our First Test Method");
// Enter username as demo
driver.findElement(By.name("userName")).sendKeys(username);
// Enter password as demo
driver.findElement(By.name("password")).sendKeys(password);
// Click Sign in button
driver.findElement(By.name("login")).click();
// Assert if Sign Off is displayed after logging in
Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
System.out.println("First Test Case Passed");
}
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- Naming our Suite as OurFirstSuite -->
<suite name="OurFirstSuite" verbose="1">
<!-- Naming our Test as Functional. We can have as many tests -->
<test name="Functional">
<!-- Include required groups to execute -->
<groups>
<!-- group = login -->
<run>
<include name="login" />
</run>
</groups>
<!-- Include classes where groups are mentioned -->
<classes>
<class name="com.selenium.testcase.FirstTestNScript" />
</classes>
</test>
</suite>
There is one more way of passing parameters from ‘testing.xml’ and that is by using ‘org.testng.ITestContext’. In the ‘testing.xml’ file, we will pass parameters for url and ‘Chrome Driver location.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<!-- Naming our Suite as OurFirstSuite -->
<suite name="OurFirstSuite" verbose="1">
<!-- Passing url and chrome driver location -->
<parameter name="url" value="http://newtours.demoaut.com"></parameter>
<parameter name="chromeDriver" value="/Chrome/chromedriver"></parameter>
<!-- Naming our Test as Functional. We can have as many tests -->
<test name="Functional">
<!-- Adding parameters at test level -->
<parameter name="username" value="demo"></parameter>
<parameter name="password" value="demo"></parameter>
<!-- Include required groups to execute -->
<groups>
<!-- group = login -->
<run>
<include name="login" />
</run>
</groups>
<!-- Include classes where groups are mentioned -->
<classes>
<class name="com.selenium.testcase.FirstTestNScript" />
</classes>
</test>
</suite>
We will make below changes to our ‘FirstTestNScript’ script. We will use ITestContext to pass data from ‘testng.xml’ file to our script. Notice that we removed unwanted methods from our ‘FirstTestNScript’ script.
package com.selenium.testcase;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Parameters;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.ITestContext;
public class FirstTestNScript {
WebDriver driver;
String url;
String chromeDriverLocation;
@Test(groups = "login")
@Parameters({"username","password"})
public void firstTestCase(String username, String password) {
System.out.println("This is our First Test Method");
// Enter username as demo
driver.findElement(By.name("userName")).sendKeys(username);
// Enter password as demo
driver.findElement(By.name("password")).sendKeys(password);
// Click Sign in button
driver.findElement(By.name("login")).click();
// Assert if Sign Off is displayed after logging in
Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
System.out.println("First Test Case Passed");
}
@Test(dataProvider = "registerDP", priority=1)
public void secondTestCase(String fname, String lname, String username, String pwd, String cpwd) {
System.out.println("This is Second Test Method");
// Click Register link in Home page
driver.findElement(By.linkText("REGISTER")).click();
// Enter first name as fname
driver.findElement(By.name("firstName")).sendKeys(fname);
// Enter last name as lname
driver.findElement(By.name("lastName")).sendKeys(lname);
// Enter username as user
driver.findElement(By.name("email")).sendKeys(username);
// Enter password as pwd
driver.findElement(By.name("password")).sendKeys(pwd);
// Enter confirm password as cpwd
driver.findElement(By.name("confirmPassword")).sendKeys(cpwd);
// Click Submit to complete registration process
driver.findElement(By.name("register")).click();
System.out.println("Second Test Case Passed");
}
// Assign WebDriver
@BeforeMethod(groups = "login")
public void beforeMethod(ITestContext context) {
System.out.println("This is Before Method");
url = context.getCurrentXmlTest().getParameter("url");
// Assign WebDriver to ChromeDriver
driver = new ChromeDriver();
// Maximize window
driver.manage().window().maximize();
// Navigate to New Tours URL
driver.get(url);
}
@AfterMethod(groups = "login")
public void afterMethod() {
System.out.println("This is After Method");
// Close and quit the driver to close the Browser
driver.close();
driver.quit();
}
// DataProvider method for login data
@DataProvider
public Object[][] loginDP() {
// username = demo, passowrd = demo
return new Object[][] { { "demo", "demo" }, { "demo", "demo" } };
}
// DataProvider method for registering
@DataProvider
public Object[][] registerDP() {
// first name = fname, last name = lname, username = user, password =
// pwd, confirm password = pwd
return new Object[][] { { "fname", "lname", "user", "pwd", "pwd" } };
}
// Setting System Properties
@BeforeTest(groups = "login")
public void beforeTest(ITestContext context) {
System.out.println("This is Before Test");
chromeDriverLocation = context.getCurrentXmlTest().getParameter("chromeDriver");
// Setting System properties
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + chromeDriverLocation);
}
}
In the above code, we are using ITestContext object to pass parameters from ‘testng.xml’ file to our script. We declared two String variables ‘url’ and ‘chromeDriverLocation’ and later assigning the values in ‘@BeforeTest’ and ‘@BeforeMethod’ methods. After successful execution, below results are displayed. Notice that all parameters are passed correctly from ‘testng.xml’ file and only ‘firstTestCase’ is executed.
With this we came to an end of TestNG tutorial. The topic is huge and it requires a constant practice to master it. Hope you were able to follow most of it.
Now that we learned the workflow in a TestNG script, let’s update our ‘FirstTestNScript’ script to include two test cases that we mentioned in our last post.
First Test Case:Navigate to New Tours application and perform successful login. Second Test Case:Navigate to New Tours application and perform successful registration. Click ‘Register’ link to go Register Page. Enter first name, last name, username, password, confirm password and click Submit to complete registration process.
This is how our test website looks like:
First step is to create some test data for our first test case. It will require an username and password as test data. To accommodate this, we will update the ‘@DataProvider‘ method with a new name and we will also use two sets of test data (even though it’s same data). This will allow our test to execute twice with different set of data. Below is the code for the ‘@DataProvider‘ method.
//DataProvider method for login data
@DataProvider
public Object[][] loginDP() {
//username = demo, passowrd = demo
return new Object[][] {{"demo", "demo"}, {"demo", "demo"}};
}
Every selenium script starts with a driver initialization. We will initialize the WebDriver inside our class but outside of all methods.
Next step is to set the system properties. System properties setting needs to be done before assigning the WebDriver to a browser driver. In our test, we will set the system properties inside ‘@BeforeTest‘ method as we are not changing the browser type. However you can set the property inside ‘@Test‘ method also. Below is the code for ‘@BeforeTest‘.
//Setting System Properties
@BeforeTest
public void beforeTest() {
System.out.println("This is Before Test");
// Setting System properties
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
}
After setting the property, next step is to assign the WebDriver to a browser driver. Since we are planning to use the same browser type for both of our tests, we will do the browser assigning part in ‘@BeforeMethod‘. Note that ‘@Before/AfterMethod‘ always execute before and after of every ‘@Test‘ method. Below is the code for ‘@BeforeMethod‘.
//Assign WebDriver
@BeforeMethod
public void beforeMethod() {
System.out.println("This is Before Method");
// Assign WebDriver to ChromeDriver
driver = new ChromeDriver();
//Maximize window
driver.manage().window().maximize();
//Navigate to New Tours URL
driver.get("http://newtours.demoaut.com");
}
Let’s create our first test case. We will use ‘@Test‘ method for our logic. For better understanding, we will update our ‘@Test‘ method with a new name and we will also use the new Data Provider. The ‘@Test‘ method will have new set of parameters. By default TestNG provides Integer type and String type parameters. Since our Data Provider has only String types, we will update the same in ‘@Test‘ method also.
@Test(dataProvider = "loginDP")
public void firstTestCase(String username, String password) {
System.out.println("This is our First Test Method");
//Enter username as demo
driver.findElement(By.name("userName")).sendKeys(username);
//Enter password as demo
driver.findElement(By.name("password")).sendKeys(password);
//Click Sign in button
driver.findElement(By.name("login")).click();
//Assert if Sign Off is displayed after logging in
Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
System.out.println("First Test Case Passed");
}
Once the test is completed, next step is to close the driver. As mentioned, ‘@AfterMethod‘ comes after the ‘@Test‘ method, so the best way is to update the ‘@AfterMethod‘ with the closing codes. Below is the code for ‘@AfterMethod‘ method.
@AfterMethod
public void afterMethod() {
System.out.println("This is After Method");
// Close and quit the driver to close the Browser
driver.close();
driver.quit();
}
Now that we completed our first test case, let’s execute the script by doing a right click on ‘FirstTestNScript’ and click Run As -> 2.TestNG Test.
As we complete our execution we will see following messages in the console.
Now that our first test case is completed, let’s focus on our second test case. Second Test Case requires a different data set up. For that we will create a new Data Provider. Below is the code for a new ‘@DataProvider‘ method. We will name it as ‘registerDP’. Notice that we used only one set of data. This means the second test case will execute only once.
//DataProvider method for registering
@DataProvider
public Object[][] registerDP() {
/*first name = fname, last name = lname, username = user, password = pwd, confirm password = pwd */
return new Object[][] {{ "fname","lname","user","pwd","pwd"}};
}
To create a new test case, we will have to create a new ‘@Test‘ method. As mentioned, the logic of a test case is always written in ‘@Test‘ method. Below is the code for a new ‘@Test‘ method. Notice that it has a name “secondTestCase” and it uses a new Data Provider “registerDP”. The parameters are also updated.
@Test(dataProvider = "registerDP")
public void secondTestCase(String fname, String lname, String username, String pwd, String cpwd){
System.out.println("This is Second Test Method");
//Click Register link in Home page
driver.findElement(By.linkText("REGISTER")).click();
//Enter first name as fname
driver.findElement(By.name("firstName")).sendKeys(fname);
//Enter last name as lname
driver.findElement(By.name("lastName")).sendKeys(lname);
//Enter username as user
driver.findElement(By.name("email")).sendKeys(username);
//Enter password as pwd
driver.findElement(By.name("password")).sendKeys(pwd);
//Enter confirm password as cpwd
driver.findElement(By.name("confirmPassword")).sendKeys(cpwd);
//Click Submit to complete registration process
driver.findElement(By.name("register")).click();
System.out.println("Second Test Case Passed");
}
Now that our second ‘@Test‘ method is also complete, we can run our ‘FirstTestNScript’ script to execute both of our test cases. Notice that for second test case we don’t have to update ‘@Before/AfterMethod‘ as the same methods will be executed for both ‘@Test‘ methods. Below is the test results after execution is complete. Notice that first test case runs twice and second test case runs only once. This is because the Data Providers are defined in that manner.
If we refresh the ‘test-output‘ folder, there will be many HTML reports. We can view them by doing a right click and Open With -> Web Browser.
Complete code for this project is provided below. Unused annotations can be removed.
package com.selenium.testcase;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.BeforeClass;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.AfterSuite;
public class FirstTestNScript {
WebDriver driver;
@Test(dataProvider = "loginDP")
public void firstTestCase(String username, String password) {
System.out.println("This is our First Test Method");
// Enter username as demo
driver.findElement(By.name("userName")).sendKeys(username);
// Enter password as demo
driver.findElement(By.name("password")).sendKeys(password);
// Click Sign in button
driver.findElement(By.name("login")).click();
// Assert if Sign Off is displayed after logging in
Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
System.out.println("First Test Case Passed");
}
@Test(dataProvider = "registerDP")
public void secondTestCase(String fname, String lname, String username, String pwd, String cpwd) {
System.out.println("This is Second Test Method");
// Click Register link in Home page
driver.findElement(By.linkText("REGISTER")).click();
// Enter first name as fname
driver.findElement(By.name("firstName")).sendKeys(fname);
// Enter last name as lname
driver.findElement(By.name("lastName")).sendKeys(lname);
// Enter username as user
driver.findElement(By.name("email")).sendKeys(username);
// Enter password as pwd
driver.findElement(By.name("password")).sendKeys(pwd);
// Enter confirm password as cpwd
driver.findElement(By.name("confirmPassword")).sendKeys(cpwd);
// Click Submit to complete registration process
driver.findElement(By.name("register")).click();
System.out.println("Second Test Case Passed");
}
// Assign WebDriver
@BeforeMethod
public void beforeMethod() {
System.out.println("This is Before Method");
// Assign WebDriver to ChromeDriver
driver = new ChromeDriver();
// Maximize window
driver.manage().window().maximize();
// Navigate to New Tours URL
driver.get("http://newtours.demoaut.com");
}
@AfterMethod
public void afterMethod() {
System.out.println("This is After Method");
// Close and quit the driver to close the Browser
driver.close();
driver.quit();
}
// DataProvider method for login data
@DataProvider
public Object[][] loginDP() {
// username = demo, passowrd = demo
return new Object[][] { { "demo", "demo" }, { "demo", "demo" } };
}
// DataProvider method for registering
@DataProvider
public Object[][] registerDP() {
// first name = fname, last name = lname, username = user, password =
// pwd, confirm password = pwd
return new Object[][] { { "fname", "lname", "user", "pwd", "pwd" } };
}
@BeforeClass
public void beforeClass() {
System.out.println("This is Before Class");
}
@AfterClass
public void afterClass() {
System.out.println("This is After Class");
}
// Setting System Properties
@BeforeTest
public void beforeTest() {
System.out.println("This is Before Test");
// Setting System properties
System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/Chrome/chromedriver");
}
@AfterTest
public void afterTest() {
System.out.println("This is After Test");
}
@BeforeSuite
public void beforeSuite() {
System.out.println("This is Before Suite");
}
@AfterSuite
public void afterSuite() {
System.out.println("This is After Suite");
}
}
With this we came to end on TestNG scripting. In the next post we will focus on configuring our test case with the help of testing.xml. Please share your comments. Happy Learning!
Additional Information:
There are many ways to configure the ‘@Test‘ method. Like for instance we can decide on which order the tests need to be executed, similarly we can group our tests in the way we want. For example in our above ‘FirstTestNScript’ script, if we want to execute the second test case first and then execute the first test case, we can easily configure this by setting the priority inside ‘@Test‘ annotation. Below will be code if we are setting the priority.
@Test(dataProvider = "loginDP", priority=2)
public void firstTestCase(String username, String password) {
System.out.println("This is our First Test Method");
// Enter username as demo
driver.findElement(By.name("userName")).sendKeys(username);
// Enter password as demo
driver.findElement(By.name("password")).sendKeys(password);
// Click Sign in button
driver.findElement(By.name("login")).click();
// Assert if Sign Off is displayed after logging in
Assert.assertTrue(driver.findElement(By.linkText("SIGN-OFF")).isDisplayed());
System.out.println("First Test Case Passed");
}
@Test(dataProvider = "registerDP", priority=1)
public void secondTestCase(String fname, String lname, String username, String pwd, String cpwd) {
System.out.println("This is Second Test Method");
// Click Register link in Home page
driver.findElement(By.linkText("REGISTER")).click();
// Enter first name as fname
driver.findElement(By.name("firstName")).sendKeys(fname);
// Enter last name as lname
driver.findElement(By.name("lastName")).sendKeys(lname);
// Enter username as user
driver.findElement(By.name("email")).sendKeys(username);
// Enter password as pwd
driver.findElement(By.name("password")).sendKeys(pwd);
// Enter confirm password as cpwd
driver.findElement(By.name("confirmPassword")).sendKeys(cpwd);
// Click Submit to complete registration process
driver.findElement(By.name("register")).click();
System.out.println("Second Test Case Passed");
}
Similarly there are many other ways by which we can configure the ‘@Test‘ method. Visit the below link for more information: