In this blog post, we’ll present a live example of writing data-driven tests using Selenium Webdriver. With the data-driven testing approach, we can create a scalable test framework and run an extensive no. of test cases.
In this tutorial, we’ll automate a demo website to book a flight using Selenium Webdriver and TestNG. Also, we’ve now upgraded this tutorial to use the Selenium 3.0 libraries and the Gecko driver for Firefox.
In addition to the data-driven automation, we’ve dealt with some quite essential features of Selenium Webdriver in this post. To name a few of these concepts are as follows.
- Handling alerts in Selenium 3.0 using a background thread
- Simulating the Autocomplete feature without using the Actions class
- Selecting a date from the date picker calendar
- Use of TestNG annotation like @DataProvider, @BeforeSuite, @AfterSuite, @BeforeTest, @AfterTest, @BeforeMethod, @AfterMethod, and @Test
- Waiting for the next page to load and process its elements
Honestly, working on this tutorial was a challenging and excellent learning experience.
Also Read – Selecting a date from the date picker calendar
Create a mini data-driven test suite in Selenium 3.0
Follow the below steps for writing data-driven tests using the Gecko driver in Selenium 3.0.
Understand the use case for writing data-driven tests
We’ll write data-driven tests to simulate flight booking using a demo website. Here is the complete use case, which we’ll automate using Selenium 3.0.
- Open the demo website to book the flight tickets.
- Read the user details from the external data source (XLS sheet).
- Parse details like source, destination, date of departure and arrival, class, and the no. of people traveling.
- Fill out the details to initiate the flight booking.
- Simulate the flight booking procedure for each row read from the XLS sheet.
- After completing all operations, the web page should be closed.
Create a Selenium TestNG project in Eclipse
If you are already familiar with Eclipse and Selenium, then you can follow the below steps.
- Create a Java project in Eclipse and name it as DataDrivenTestSuite. Browse it and make sure it contains the src folder. If it’s not there, then create the one.
Note – Make sure the correct JDK version is set as the default JRE.
- Add the following two packages under the src folder and create empty Java files, as mentioned.
- com.techbeamers.seleniumtestng.datadriventest
- Create the blank DataDrivenTest.java file.
- com.techbeamers.seleniumtestng.datamanager
- Create the blank DataManager.java file.
- com.techbeamers.seleniumtestng.datadriventest
Note – You can check out the source code of these files towards the end of this post.
- Create a Resources folder inside your project. We’ll use it later to store the data file for flight booking.
- Now, add the following libraries as external Jar files.
- Selenium 3.0 standalone jar
- poi-3.14-20160307.jar
- poi-ooxml-3.14-20160307.jar
- poi-ooxml-schemas-3.14-20160307
- xmlbeans-2.6.0.jar.
Note – You can download the Selenium 3.0 and Gecko driver executable from here.
- Also, right-click on your project, then go to Build Path >> Add Libraries and select TestNG. In Selenium 3.0 projects, you need to add the TestNG reference explicitly.
- You can create a <testng.xml> file again by right-clicking your project and using the TestNG >> Convert to TestNG option.
Note – TestNG option will only be available if you’ve installed the TestNG plugin in Eclipse.
If you are a newbie and want a detailed overview of creating a simple Webdriver project in Eclipse, then please refer to these six steps to learn it quickly.
So these were a few basic steps to get the skeleton ready for writing the data-driven tests.
Prepare an XLS sheet for data-driven testing
Since we’ll be writing data-driven tests in the next sections, it’s mandatory to define a data source. We’ll use an XLS sheet to hold the data values. See the example below.
You need to get the data values shown in the image and put them in an Excel file. Then, save the Excel sheet as test_input.xlsx on your system. And finally, move it to the Resources folder inside your project.
Add code to enable Selenium 3.0 support
You’ll need to add the following lines of code to allow for Selenium 3.0 support. Make sure to set the path of the Gecko driver for Firefox.
// Selenium-TestNG Suite Initialization @BeforeSuite public void suiteSetup() { System.out.println("suiteSetup"); System.setProperty("webdriver.gecko.driver", driverPath + "geckodriver.exe"); DesiredCapabilities caps = new DesiredCapabilities(); caps.setCapability("marionette", true); driver = new FirefoxDriver(); startMonitor(); }
How to handle alerts in Selenium 3.0 on a background thread?
You might be wondering why should we be bothered about the alert. It is because the demo website that we are using keeps on generating authentication alerts. And we need to suppress them all to process the flight bookings placidly.
First of all, you’ll need to write a simple method to purge any alert that appears on the web page. So here is the code for the purgeAllAlerts() method.
// Method to remove alerts from the web page public void purgeAllAlerts() { try { Thread.sleep(purgeInterval); Alert alert = driver.switchTo().alert(); if (alert != null) alert.dismiss(); } catch (Exception ex) { // Intentionally left blank. } }
Now, here is the tricky part, we want to spawn a worker thread to purge any alert that comes out silently. Please check out the code below to see the thread that calls the purgeAllAlerts() method at specified intervals.
// Method to start background thread for removing alerts public void startMonitor() { System.out.println("enter into AlertMonitor()."); keepAlive = true; Thread t = new Thread(new Runnable() { public void run() { for (;;) { purgeAllAlerts(); if (!keepAlive) break; } System.out.println("exit from AlertMonitor() thread."); } }); t.start(); System.out.println("exit from AlertMonitor()."); } // Method to stop alert monitor thread public void stopMonitor() { keepAlive = false; }
Check the detailed steps here: Create a TestNG Project in Selenium.
The startMonitor() method creates a background thread to dismiss the authentication alerts. The thread depends on the state of a variable named keepAlive to decide its lifetime. The above code also has a stopMonitor() method, which sets the keepAlive to false and stops the thread.
What is Autocomplete and how to handle it in Selenium 3.0?
Probably you’ve seen that most travel websites are using the Autocomplete text boxes for user input. It facilitates users in choosing an option from a pre-populated list of values based on the text that the user enters. It mainly concentrates on providing suggestions to users while typing into the field. So we’ll be writing data-driven tests to automate this scenario.
Let’s now see an example. When we enter any text in the textbox supporting Autocomplete, it displays a no. of options containing the related text. We can then select a value from that pre-populated list. You can see that we are using a demo travel portal to automate the flight booking procedure using Selenium Webdriver.
Note – Selenium 3.0 has a known issue with the Actions class. Its methods (like moveToElement()) fails to work in Selenium 3.0. So we’ve used an alternative method to support the autocomplete feature.
The Old method which doesn’t work in Selenium 3.0
// Insert data into Flying from text box WebElement sourceObj = driver.findElement(By.id("intlDeptCode")); Actions builder = new Actions(driver); Actions seriesOfActions = builder.moveToElement(sourceObj).click().sendKeys(SourcePattern); seriesOfActions.perform();
The above method is still applicable to users who are using Selenium 2.0 for automation testing.
The Newer method which works in both Selenium 2.0 and Selenium 3.0
// Insert data into Flying from text box driver.findElement(By.id("intlDeptCode")).click(); driver.findElement(By.id("intlDeptCode")).sendKeys(SourcePattern); driver.findElement(By.id("intlDeptCode")).click();
How to select the dates from the calendar?
In this tutorial, we are also covering how to use date pickers present on the pages. The date picker is just like a table with some set of rows and columns. To select a date, you have to choose the desired month and year from their drop-downs. For the day, you’ll first navigate to the cell displaying the target day and then click to select it.
Here is the piece of code that selects the day of departure and arrival from the (date picker) calendar control.
// Enter Departing date // split date to take out month,year,day in the date // String depdate="24-Feb-2016"; String depdate = departdate; String[] temp; String delimiter = "-"; temp = depdate.split(delimiter); for (int i = 0; i < temp.length; i++) System.out.println(temp[i]); // Click on textbox so that datepicker will come WebElement Cal = driver.findElement(By.id("deptDate")); Cal.click(); // Select the month in the calender Select oSelect = new Select(driver.findElement(By.className("ui-datepicker-month"))); oSelect.selectByVisibleText(temp[1]); // Get the year difference between current year and year to set in // calendar int yearDiff = Integer.parseInt(temp[2]) - Calendar.getInstance().get(Calendar.YEAR); if (yearDiff != 0) { Select ySelect = new Select(driver.findElement(By.className("ui-datepicker-year"))); ySelect.selectByVisibleText(temp[2]); } /* * DatePicker is a table.So navigate to each cell If a particular cell * matches date value then select it */ WebElement dateWidget = driver.findElement(By.id("ui-datepicker-div")); List<WebElement> rows = dateWidget.findElements(By.tagName("tr")); List<WebElement> columns = dateWidget.findElements(By.tagName("td")); for (WebElement cell : columns) { // Select Date if (cell.getText().equals(temp[0])) { cell.findElement(By.linkText(temp[0])).click(); break; } }
How to wait for the next page to load?
When the user fills in the flight details and clicks to submit, he has to wait for the next page to load to view the flight options. We need this code to verify the various flight options available on the next screen.
Below is the code which demonstrates one of the best ways to address this case.
// Method to wait for the next page to load public void waitForPageLoaded() { int maxWait = 30; long tStart = System.currentTimeMillis(); long elapsedSeconds = 0; for (; elapsedSeconds < maxWait;) { try { purgeAllAlerts(); driver.manage().timeouts().implicitlyWait(implicitlyWait, TimeUnit.SECONDS); if (driver.getCurrentUrl().contains("ErrorCode=1052")) { // Error page, exit immediately instead of keep on waiting. System.out.println("\nErrorCode=1052"); return; } driver.findElement(By.cssSelector("p.ng-binding")); System.out.println("\nNew page successfully loaded"); return; } catch (WebDriverException ex) { // Intentionally left blank. } elapsedSeconds = (System.currentTimeMillis() - tStart) / 1000; } System.out.println("\n waitForPageLoaded(): elapsedSeconds= " + elapsedSeconds); }
Configurable waits
You can set the Wait values at your convenience. The implicit wait is the max response time for any Webdriver command and the purge interval is the delay in executing the thread.
WebDriver driver = null; public static boolean keepAlive = true; public static long purgeInterval = 10; // in milliseconds public static long implicitlyWait = 20; // in seconds public static String driverPath = "C:\\workspace\\tools\\selenium\\geckodriver-v0.11.1-win64\\";
The full source code of the project
In this flight booking sample, first of all, we’ll fill the source and destination fields. Both of these are Autocomplete-enabled. So as we enter a partial text, a list will emerge suggesting options. We’ll iterate through the list and choose the desired option.
Probably now you want to see the code of the Java class that we created for flight booking. Please copy and paste the below code inside the DataDrivenTest.Java file left blank in the beginning.
Sample Code of DataDrivenTest.java
package com.techbeamers.seleniumtestng.datadriventest; import java.io.IOException; import java.util.Calendar; import java.util.List; import java.util.concurrent.TimeUnit; import org.openqa.selenium.Alert; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.Select; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeSuite; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import com.techbeamers.seleniumtestng.datamanager.DataManager; public class DataDrivenTest { WebDriver driver = null; public static boolean keepAlive = true; public static long purgeInterval = 10; // in milliseconds public static long implicitlyWait = 20; // in seconds public static String driverPath = "C:\\workspace\\tools\\selenium\\geckodriver-v0.11.1-win64\\"; // Selenium-TestNG Suite Initialization @BeforeSuite public void suiteSetup() { System.out.println("suiteSetup"); System.setProperty("webdriver.gecko.driver", driverPath + "geckodriver.exe"); DesiredCapabilities caps = new DesiredCapabilities(); caps.setCapability("marionette", true); driver = new FirefoxDriver(); startMonitor(); } // Selenium-TestNG Suite cleanup @AfterSuite public void suiteTeardown() { System.out.println("suiteTeardown"); driver.close(); driver.quit(); } @BeforeMethod public void beforeTest() throws InterruptedException { System.out.println("Open Browser"); driver.get("http://beta.wakanow.com/flightsv2"); Thread.sleep(1000); driver.manage().window().maximize(); System.out.println("exit from openBrowser()"); } @AfterMethod public void afterTest() { // Intentionally left blank. } // Selenium-TestNG Execution Engine @Test(dataProvider = "FlightBookingData") public void flightDeals(String SourcePattern, String Source, String DestPattern, String Destination, String departdate, String returndate, String adults, String children, String infants, String ticketClass) throws InterruptedException, IOException { driver.manage().timeouts().implicitlyWait(implicitlyWait, TimeUnit.SECONDS); // Click on roundtrip radio button WebElement radioBtn = driver.findElement(By.id("roundtrip")); radioBtn.click(); // Insert data into Flying from text box driver.findElement(By.id("intlDeptCode")).click(); driver.findElement(By.id("intlDeptCode")).sendKeys(SourcePattern); driver.findElement(By.id("intlDeptCode")).click(); // Working with Autocomplete text in Flying from text box WebElement autoOptions = driver.findElement(By.className("ac_results")); List<WebElement> optionsToSelect = autoOptions.findElements(By.tagName("li")); for (WebElement option : optionsToSelect) { if (option.getText().contains(Source)) { System.out.println("Trying to select: "); option.click(); break; } } // Remove duplicate autoOptions object so that Flying to can work ((JavascriptExecutor) driver).executeScript("arguments[0].parentNode.removeChild(arguments[0])", autoOptions); // Insert data into Flying to text box driver.findElement(By.id("intlArrvCode")).click(); driver.findElement(By.id("intlArrvCode")).sendKeys(DestPattern); driver.findElement(By.id("intlArrvCode")).click(); // Working with Autocomplete text in Flying to text box WebElement autoOptions1 = driver.findElement(By.className("ac_results")); System.out.println("Selected the Flying to List"); WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.visibilityOf(autoOptions1)); List<WebElement> optionsToSelect1 = autoOptions1.findElements(By.tagName("li")); for (WebElement option : optionsToSelect1) { String var = option.getText(); System.out.println("Trying to select1: " + var); if (option.getText().contains(Destination)) { System.out.println("Trying to select123: "); option.click(); break; } } // Enter Departing date // split date to take out month,year,day in the date // String depdate="24-Feb-2016"; String depdate = departdate; String[] temp; String delimiter = "-"; temp = depdate.split(delimiter); for (int i = 0; i < temp.length; i++) System.out.println(temp[i]); // Click on textbox so that datepicker will come WebElement Cal = driver.findElement(By.id("deptDate")); Cal.click(); // Select the month in the calender Select oSelect = new Select(driver.findElement(By.className("ui-datepicker-month"))); oSelect.selectByVisibleText(temp[1]); // Get the year difference between current year and year to set in // calendar int yearDiff = Integer.parseInt(temp[2]) - Calendar.getInstance().get(Calendar.YEAR); if (yearDiff != 0) { Select ySelect = new Select(driver.findElement(By.className("ui-datepicker-year"))); ySelect.selectByVisibleText(temp[2]); } /* * DatePicker is a table.So navigate to each cell If a particular cell * matches date value then select it */ WebElement dateWidget = driver.findElement(By.id("ui-datepicker-div")); List<WebElement> rows = dateWidget.findElements(By.tagName("tr")); List<WebElement> columns = dateWidget.findElements(By.tagName("td")); for (WebElement cell : columns) { // Select Date if (cell.getText().equals(temp[0])) { cell.findElement(By.linkText(temp[0])).click(); break; } } // Enter Arriving date // split date to take out month,year,day in the date // String arrdate="26-Feb-2016"; String arrdate = returndate; String[] temp1; String delimit = "-"; temp1 = arrdate.split(delimit); for (int i = 0; i < temp1.length; i++) System.out.println(temp1[i]); // Click on textbox so that datepicker will come WebElement Calen = driver.findElement(By.id("arrvDate")); Calen.click(); // Select the month in the calender Select mSelect = new Select(driver.findElement(By.className("ui-datepicker-month"))); mSelect.selectByVisibleText(temp1[1]); // Get the year difference between current year and year to set in // calander int yearDif = Integer.parseInt(temp1[2]) - Calendar.getInstance().get(Calendar.YEAR); if (yearDif != 0) { Select ySelect1 = new Select(driver.findElement(By.className("ui-datepicker-year"))); ySelect1.selectByVisibleText(temp1[2]); } /* * DatePicker is a table.So navigate to each cell If a particular cell * matches date value then select it */ WebElement dateWidget1 = driver.findElement(By.id("ui-datepicker-div")); List<WebElement> row = dateWidget1.findElements(By.tagName("tr")); List<WebElement> column = dateWidget1.findElements(By.tagName("td")); for (WebElement cell : column) { // Select Date if (cell.getText().equals(temp1[0])) { cell.findElement(By.linkText(temp1[0])).click(); break; } } // Select Ticket class Select tktClass = new Select(driver.findElement(By.className("ticketclass"))); tktClass.selectByVisibleText(ticketClass); // Select Adults Select adult = new Select(driver.findElement(By.id("adults"))); adult.selectByVisibleText(adults); // Select Infants Select infant = new Select(driver.findElement(By.id("infants"))); infant.selectByVisibleText(infants); // Select Children Select child = new Select(driver.findElement(By.id("fltchildren"))); child.selectByVisibleText(children); // Click on Search button driver.findElement(By.id("btnSearch")).click(); waitForPageLoaded(); int iFlightOption = 0; try { String results = driver.findElement(By.cssSelector("p.ng-binding")).getText(); iFlightOption = Integer.parseInt(results.split(" ")[0]); } catch (Exception ex) { iFlightOption = 0; } System.out.println(" Flight option found: " + iFlightOption); Assert.assertTrue(iFlightOption > 0, "No flight option found!"); } // Selenium-TestNG Data Provider @DataProvider(name = "FlightBookingData") public Object[][] datasupplier() throws Exception { final String xlsxFile = System.getProperty("user.dir") + "\\Resources\\test_input.xlsx"; Object[][] arrayObject = DataManager.getExcelData(xlsxFile, "Sheet1"); return arrayObject; } // Utility Functions // Method to remove alerts from the web page public void purgeAllAlerts() { try { Thread.sleep(purgeInterval); Alert alert = driver.switchTo().alert(); if (alert != null) alert.dismiss(); } catch (Exception ex) { // Intentionally left blank. } } // Method to start background thread for removing alerts public void startMonitor() { System.out.println("enter into AlertMonitor()."); keepAlive = true; Thread t = new Thread(new Runnable() { public void run() { for (;;) { purgeAllAlerts(); if (!keepAlive) break; } System.out.println("exit from AlertMonitor() thread."); } }); t.start(); System.out.println("exit from AlertMonitor()."); } // Method to stop alert monitor thread public void stopMonitor() { keepAlive = false; } // Method to wait for the next page to load public void waitForPageLoaded() { int maxWait = 30; long tStart = System.currentTimeMillis(); long elapsedSeconds = 0; for (; elapsedSeconds < maxWait;) { try { purgeAllAlerts(); driver.manage().timeouts().implicitlyWait(implicitlyWait, TimeUnit.SECONDS); if (driver.getCurrentUrl().contains("ErrorCode=1052")) { // Error page, exit immediately instead of keep on waiting. System.out.println("\nErrorCode=1052"); return; } driver.findElement(By.cssSelector("p.ng-binding")); System.out.println("\nNew page successfully loaded"); return; } catch (WebDriverException ex) { // Intentionally left blank. } elapsedSeconds = (System.currentTimeMillis() - tStart) / 1000; } System.out.println("\n waitForPageLoaded(): elapsedSeconds= " + elapsedSeconds); } }
Sample Code for DataManager.java
Here is the sample code for the DataManager.java file. It’ll read the flight booking details (test data) from the data source. And it’ll then pick a date available in the test data.
Copy and paste the below code inside the DataManager.Java file of your project.
package com.techbeamers.seleniumtestng.datamanager; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; public class DataManager { private static XSSFSheet ExcelWSheet; private static XSSFWorkbook ExcelWBook; private static XSSFCell Cell; private static XSSFRow Row; // This method is to set the File path and to open the Excel file, Pass // Excel Path and Sheet name as Arguments to this method public static Object[][] getExcelData(String FilePath, String SheetName) throws Exception { String[][] tabArray = null; try { // Access the required test data sheet FileInputStream ExcelFile = new FileInputStream(FilePath); ExcelWBook = new XSSFWorkbook(ExcelFile); ExcelWSheet = ExcelWBook.getSheet(SheetName); int totalNoOfCols = 10; int totalNoOfRows = ExcelWSheet.getLastRowNum(); tabArray = new String[totalNoOfRows][totalNoOfCols]; for (int i = 1; i <= totalNoOfRows; i++) { for (int j = 0; j < totalNoOfCols; j++) { Cell = ExcelWSheet.getRow(i).getCell(j); int cel_Type = Cell.getCellType(); switch (cel_Type) { case XSSFCell.CELL_TYPE_NUMERIC: // 0 if (DateUtil.isCellDateFormatted(Cell)) { DateFormat df = new SimpleDateFormat("dd-MMM-yyyy"); tabArray[i - 1][j] = df.format(Cell.getDateCellValue()); } else { tabArray[i - 1][j] = String.format("%d", (long) Cell.getNumericCellValue()); } break; case XSSFCell.CELL_TYPE_STRING: // 1 tabArray[i - 1][j] = Cell.getStringCellValue(); break; } } } } catch (FileNotFoundException e) { System.out.println("Could not read the Excel sheet"); e.printStackTrace(); } catch (IOException e) { System.out.println("Could not read the Excel sheet"); e.printStackTrace(); } return tabArray; } }
Summary – Mini Data-Driven Test Suite in Selenium
It was a comprehensive tutorial on writing data-driven tests using Selenium Webdriver and TestNG. You can try the code with the latest Selenium version 4 as well. Do report to us if you face any issues. We’ll help you fix those properly.
And hopefully, you all would like it and be able to use the concept in your projects. Lastly, render your support by sharing this post on social media (Facebook/Twitter). It will encourage us to keep creating useful content for free.
All the Best,
TechBeamers