Selenium Webdriver -Implementing Page Object Model with TestNG and Data driven framework — Post#2

In my previous post, i showed how i implemented the framework. Here i will talk about the issues i faced while designing it. One can learn a great deal of things from the mistakes done by others.

  1. In the  @BeforeTestCase, you can see that I’ve created object of the various classes. I call the methods of the various class in Test method using these objects. When I am in Login page, after i perform Login, i captured the window title of Landup Page. The intention was to make this title available for Landup page class, so that before I do some thing in Landup Screen  i wanted to make sure i’m in the right screen. But no matter whatever get() and set() method i used, i was not able to access the value of Landup Screen window title .
    Reason: Login class Object reference gets killed once the java control comes out of the line 58 and executes line 59 (Refer to the @Test method in last post). Thus when We  Landup page class’s method is getting executed at line#59, the window title value captured in Line#58 (Login page class) is lost.
  2. The above limitation can apply to any webelement as such, which you have captured in one class and trying to use from other class/@Test Method. It is better to declare and initialize such webelement inside the Test Class such that it statys alive till the end of Test suite execution
  3. This might be issue with version of JRE i am using. If you look at the line# 76, i’m trying to log a information in report whenever the script captures screenshot after the execution. For some reasons unknown to be, this would not get executed! There are 3 lines of code inside that Try block and the first 2 works fine. Not sure why this 3 rd line (Line#76) doesnt get executed.
    The intention was to make the screenshot location available for the user who looks at the report.

 

Selenium Webdriver -Implementing Page Object Model with TestNG and Data driven framework

For the purpose of demo here i’ve used this site – http://automationpractice.com/index.php

I wanted to keep the framework light with two main aims:

  1. Ease of maintenance and debugging
  2. Ease of reporting

While Page object model was obvious choice for Point#1, i tried to implement ExtentReports for point#2. But due to some reason, it didn’t work out well for me. So let me put a disclaimer here; If you are here to learn about ExtentReport (keyword search landed you here), please hit back button on your browser window now.

For any test automation script, it is important we use proper logging in the reports. This will not only help locate the errors easily, but also helps in debugging of the script. So instead of wasting time in create eye-catching reports with all those pie-charts and colorful graphs, i decided to keep it simple. I just focused on the primary objective – log everything that you do on the browser.

Let me talk about POM first: Again, i have not used Page factory in my framework. So if you are here to learn PageFactory, please hit the back button.

  1. In the application, there is home page, Login page and Landup page (screen that you navigate once you login)
  2. I created a eCommerce Package and in that, i added one Java class each for each of the above screens
  3. I then created a Test eCommerce Package in which i created  Java Class that will have my test cases.
  4. The 3rd package is Utility package – this will contain the functions that is used to do mundane tasks like excel file reading, that can be used in any of the above 2 packages

Data Driven framework: With the help of POM, i was able to create repository of objects in relevant classes that helped greatly in debugging, I now started thinking how to iterate my test cases for various sets of data. That’s when i went with traditional way of running the same Test cases for various input data saved in CSV files. TestNG provides the dataprovider annotation to do that. I just used APACHE poi Jar to read the data from Excel and iterate my  @Test Methods.

That’s it. I’ve pasted the code snippet below. Before trying to understand the code, i strongly suggest you to check the demo site first. This helps you to understand the framework and the code better.

eCommWebsite Package

 

Home Page class

package testEcommerceWebsite;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import pomEcommerceWebsite.eCommPageObjects_HomePage;
import pomEcommerceWebsite.eCommPageObjects_LandupPage;
import pomEcommerceWebsite.eCommPageObjects_LoginPage;
import utilityPackage.utilityClass;

public class eCommTestClass {

 //Create Objects of each screen Page object 
 eCommPageObjects_HomePage h;
 eCommPageObjects_LoginPage lo;
 eCommPageObjects_LandupPage la;

 int testCaseNum=0;
 String testCaseType;
 WebDriver d;

 @BeforeClass
 public void setup(){
 File file = new File("C:/Selenium/IEDriverServer.exe");
 System.setProperty("webdriver.ie.driver", file.getAbsolutePath());
 d = new InternetExplorerDriver();
 d.get("http://automationpractice.com/index.php");
 Reporter.log("Browser Opened");
 h = new eCommPageObjects_HomePage(d);
 lo = new eCommPageObjects_LoginPage(d);
 la = new eCommPageObjects_LandupPage(d);

 }

 @Test(dataProvider = "dp")
 public void TestCase1(String tcType, String userName, String password) {

 testCaseType = tcType;
 testCaseNum = testCaseNum+1;
 //try{
 d.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
 h.clickOnSignIn();
 lo.enterCredentials(userName, password);
 la.signOut();
 }

 @AfterMethod
 public void td(ITestResult result) {

 if (result.getStatus() == ITestResult.FAILURE) {

 System.out.println(result.getStatus());
 TakesScreenshot ts = (TakesScreenshot)d;
 File source = ts.getScreenshotAs(OutputType.FILE);
 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime());

 try {
 FileUtils.copyFile(source, new File("C:\\Selenium\\Screesnhots\\TestCase# "+testCaseNum+"_"+timeStamp+".jpg"));
 d.navigate().to("http://automationpractice.com/index.php");
 Reporter.log("Test # " + testCaseNum + "has failed. Please check the screenshot C:\\Selenium\\Screesnhots\\TestCase# "+testCaseNum+".jpg");
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 }

 @DataProvider (name = "dp")
 public String[][] dataExtractor() throws Exception{

 utilityClass uc = new utilityClass();
 String[][] signInCredentials = uc.excelDataReader("C:\\Selenium\\TestData\\eCommTest.xlsx");
 return signInCredentials;
 }

}

Login Page Class


package pomEcommerceWebsite;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.testng.Reporter;

public class eCommPageObjects_LoginPage {

 WebElement emailAddress,password,loginButton;
 WebDriver driver;

 public eCommPageObjects_LoginPage(WebDriver d){
 this.driver = d;

 }

 public void enterCredentials(String email, String passw){

 String title = "Login - My Store";
 if (title.equals(driver.getTitle())){
 emailAddress = driver.findElement(By.id("email"));
 password = driver.findElement(By.id("passwd"));
 loginButton = driver.findElement(By.xpath("//button[@name = 'SubmitLogin'][@id = 'SubmitLogin']"));

 emailAddress.sendKeys(email);
 password.sendKeys(passw);
 loginButton.click();
 }else{
 Reporter.log("Not in Login Page");
 //org.testng.Assert.fail();
 } 
 }

}

 

Landup Page Class

 

package pomEcommerceWebsite;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.testng.Reporter;

public class eCommPageObjects_LandupPage {

WebElement logOutButton;
WebDriver driver;


public eCommPageObjects_LandupPage(WebDriver d){
this.driver = d;
}

public void signOut(){

//check whether we are in right window
String title = driver.getTitle();
if (title.equals(driver.getTitle())){
//Click Logout button
logOutButton = driver.findElement(By.xpath("//a[@title = 'Log me out'][@class = 'logout']"));
driver.manage().timeouts().implicitlyWait(2,TimeUnit.SECONDS);
logOutButton.click();
//Go to Home Screen to accept next set of credentials
driver.navigate().to("http://automationpractice.com/index.php");
}else{
Reporter.log("Not in Landup Page");
//org.testng.Assert.fail();
}
}
}

eCommTest Package : Contains all Test case suite.

Test Class

package testEcommerceWebsite;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import pomEcommerceWebsite.eCommPageObjects_HomePage;
import pomEcommerceWebsite.eCommPageObjects_LandupPage;
import pomEcommerceWebsite.eCommPageObjects_LoginPage;
import utilityPackage.utilityClass;

public class eCommTestClass {

//Create Objects of each screen Page object
eCommPageObjects_HomePage h;
eCommPageObjects_LoginPage lo;
eCommPageObjects_LandupPage la;

int testCaseNum=0;
String testCaseType;
WebDriver d;

@BeforeClass
public void setup(){
File file = new File("C:/Selenium/IEDriverServer.exe");
System.setProperty("webdriver.ie.driver", file.getAbsolutePath());
d = new InternetExplorerDriver();
d.get("http://automationpractice.com/index.php");
Reporter.log("Browser Opened");
h = new eCommPageObjects_HomePage(d);
lo = new eCommPageObjects_LoginPage(d);
la = new eCommPageObjects_LandupPage(d);

}

@Test(dataProvider = "dp")
public void TestCase1(String tcType, String userName, String password) {

testCaseType = tcType;
testCaseNum = testCaseNum+1;
//try{
d.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
h.clickOnSignIn();
lo.enterCredentials(userName, password);
la.signOut();
}

@AfterMethod
public void td(ITestResult result) {


if (result.getStatus() == ITestResult.FAILURE) {

System.out.println(result.getStatus());
TakesScreenshot ts = (TakesScreenshot)d;
File source = ts.getScreenshotAs(OutputType.FILE);
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime());

try {
FileUtils.copyFile(source, new File("C:\\Selenium\\Screesnhots\\TestCase# "+testCaseNum+"_"+timeStamp+".jpg"));
d.navigate().to("http://automationpractice.com/index.php");
Reporter.log("Test # " + testCaseNum + "has failed. Please check the screenshot C:\\Selenium\\Screesnhots\\TestCase# "+testCaseNum+".jpg");
} catch (IOException e) {
e.printStackTrace();
}
}
}

@DataProvider (name = "dp")
public String[][] dataExtractor() throws Exception{

utilityClass uc = new utilityClass();
String[][] signInCredentials = uc.excelDataReader("C:\\Selenium\\TestData\\eCommTest.xlsx");
return signInCredentials;
}

}

Here i’m performing simple login test. The test case runs as many times as the number of rows of data is there in the input data sheet. I’m passing the user id and password from the data sheet.

You can see here that I’ve created a function in AfterTest to capture the screenshot. This will get invoked only if the test case fails due to some exception. The ITestresult of TestNG is used to check the Test case status.

Initially, i wanted to design the test case such that, if i pass the parameter as “Negative Test case” through the data sheet, the test case shouldn’t fail. In other words, if i pass invalid login credentials, i know the login will not be success. But that is the expected result for the test case and i wanted the test case to pass and not to throw “Element not found exception”. But there were some issues in getting the test case result passed forcefully. So i discarded that idea. If i really want to do that, i will create separate datasheet just for negative test and make sure that all the test cases fail .

 

Utility Package: Contains a class that holds functions to perform repetitive tasks

package utilityPackage;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class utilityClass {

int testCaseNum;

//Method to read Excel Data
public String[][] excelDataReader (String fileLocation) throws IOException{

String[][] signInData = null;

//Create a object of File class to open xlsx file
File file = new File ("C:\\Selenium\\TestData\\eCommTest.xlsx");

//Create an object of FileInputStream class to read excel file
FileInputStream f = new FileInputStream(file);

//Create XLSX workbook object
Workbook signInTestData = new XSSFWorkbook(f);

//Get Sheet
String sheetName = new String("Sheet1");
Sheet testDataSheet = signInTestData.getSheet(sheetName);

//Get RowCount
int rowCount = testDataSheet.getLastRowNum();
System.out.println(testDataSheet.getLastRowNum());

//Declare row variable of type Row Class
Row row = testDataSheet.getRow(0);

//Get COlumn Count
int colCount = row.getLastCellNum();
System.out.println(colCount);

//Declare and intialize the String object, that will store excel data
signInData = new String[rowCount][colCount];

System.out.println(testDataSheet.getRow(0).getCell(1).getStringCellValue());
//Read the excel and store the value in array
//Starting reading from 2nd row, as first row is header
for (int r = 1; r<=rowCount; r++){
row = testDataSheet.getRow(r);
testCaseNum = r;
for(int c = 0;c<row.getLastCellNum();c++){
signInData [r-1][c] = row.getCell(c).getStringCellValue();
}
}
signInTestData.close();
return signInData;
}
}

 

In the next post, i will talk about what are the issues or challenges i faced in this POM framework.