A Better Way of Writing Selenium Scripts [Part – 1]

Many of us would be wordering what’s the best way to do scripting using selenium. Should we use ‘Data Driven Test Frameworks’, should we use ‘Keyword Driven Test Framework’ or should we combine both and use ‘Hybrid Test Framework’. Should we fetch the data from an excel sheet, or may be should we use XML files, or should we be more developer kind and use a Database instead. If you search on internet you will find many answers and arguments. Frankly speaking there is no defined best way to script using selenium. It all depends on the application under test, what would be the best way to test the same. Here I would be taking the the Hybrid Framework into consideration with the help of a Database to provide data to my scripts. Here I would be answering the question which arises after my post about the Hybrid framework which I wrote earlier sometime. I will explain a way to use the framework in an efficient manner. Contrary from my post about Hybrid Framework, the examples I would be using here would be of WebDriver as with the progress of Selenium, I have also started to explore the power of WebDriver now. So let’s start.

Something About Dividing the Test Modules:

So let’s start with going about designing test cases. What’s an efficient way of doing the same? Let’s take example of a site ‘www.yahoo.com‘ which gives details about different mobile phones. Here taking modularity into consideration, each functionality should be considered as a test module. The smaller the test module is, the better it is for automation. Always remember to divide an application to smaller modules keeping automation in mind which is different from manual testing. Here let me give an example. When you open www.yahoo.com, we can consider sign in as one test module, mail as one test module, Trending now can be one test module, the Yahoo Sites column can be one module. Now we need to design test cases in each module. Let’s take the example of sign in module. For sign in module we can write the test cases as below.

Sl No Action Expected Result
1 Click SIGN IN link in the home page. Sign in page should open with page title “Sign in to Yahoo India”
2 Check if Yahoo ID field is there Yahoo ID field should be present
3 Check if Password field is there Password field should be there
4 Check if Keep me signed in checkbox is there Keep me signed in checkbox should be present
5 Check if sign in button is present Sign in button should be present
6 Enter valid login information and click on Sign in button The user should be able to sign in and page should redirect back to yahoo home page.

Similarly many test cases are possible for this scenario. Let me limit myself here and we’ll go about automating these step for the example. Similarly we can write test cases for different test modules.

Designing data model to test different modules effectively:

How do we design our test modules and put them into the test framework so that we can test them independently. This can be achieved with above approach of designing test modules to the smallest unit so that they are independent of each other. Why do so? Let’s assume that we have designed our test cases in such a way that dependencies are there and when we run the automation script for them, the whole suite runs with all dependencies. The next question is, what’s the problem with that? Let’s say there is an enhancement in login module. Now the question arises why test let’s the search module for the same and waste time. We should be able test only the module where changes were there to achieve the effectiveness that we are looking for. Let’s consider we have 4 test modules as below.

  • Sign in
  • Search
  • Mail
  • Yahoo Site Module

Note: I’m considering only these for showing the examples only

Now comes the use of Database. Let’s create a master table which store information about different modules. Let’s call the table auto_tabData. The table looks as below.

Id Functionality TableForFunctionality
1 Sign in auto_signInData
2 Search auto_searchData
3 Mail auto_mailData
4 Yahoo Site Module auto_yahooSiteModuleData

Here in this table, for each functionality, we record the table name which hold the data for the same. Now lets design the table for Sign in functionality. Let’s say the table for Sign in functionality looks as below. (Note: The table is designed to suit the hybrid framework explained in my earlier post)

Property Locator LocatorType Value ExpectedResult Permission Name
pagetitle Yahoo! India Home Page Title
link //div[@id=’default-p_30345710_cec-bd’]/a/em xpath click Sign in link
pagetitle Sign in to Yahoo! India Sign in page title
textbox #username cssSelector Your actual username type User ID field
textbox #passwd cssSelector Your actual password type Password field
checkbox #persistent cssSelector click Keep me signed in checkbox
link //div[@id=’submit’]/button xpath click Submit button
value //div[@id=’default-p_30345710_d0c-bd’]/a/em xpath Hi, Your Name Welcome message in yahoo home page

Similarly let’s assume we have data tables for other functionalities too. Keeping all these details in mind, we’ll see in the next section on how to go about designing our test suite.

Designing the class model to accommodate the data model effectively:

In the previous section we saw the data model design. Now in this section we’ll see how to use them effectively. To do so, we should design our classes in three step. Basically there will be 3 high level classes.

  • 1st one’s purpose is to pass the functionalities which are to be tested.
  • 2nd one’s purpose is to get the table name from the DB which will be executed according to the class names passed from the 1st class and call the 3rd driver class which executes the test.
  • 3rd one is the driver class which runs the test according to the data passed from the 1st class.

Below are the code excerpts on how these classes work. Let’s name the first type of class as ‘UserDefinedTest‘. The class looks as below.

import org.testng.annotations.*;

public class UserDefinedTest {
 @Test
 public void defineTest() throws Exception {
 /*
 * Mention the Id(s), functionality(ies) here in the below 2
 * dimensional array (for multiple pages and functionalities, please
 * enter in the order of execution)
 */
 String[][] pageFuctionalities = { { "1", "Sign in" },{ "2", "search" } };
 AppTest appTest = new AppTest();
 appTest.setPageFunctionalities(pageFuctionalities);
 appTest.setBaseURL("http://www.yahoo.com/");
 appTest.setOutputFileName("outputMyTest");
 appTest.setScreenshotName("Screenshot");
 appTest.runTest();
 }
}

Now let’s name the 2nd type of class ‘AppTest‘. This class looks as below.

import org.testng.annotations.*;
import java.io.StringWriter;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("unused")
public class AppTest {
 private String pageFunctionalities[][];
 private String baseURL;
 private String outputFileName;
 private String screenshotName;

public void setPageFunctionalities(String pageFunctionalities[][]) {
 this.pageFunctionalities = pageFunctionalities;
 }

public void setBaseURL(String baseURL) {
 this.baseURL = baseURL;
 }

public void setOutputFileName(String outputFileName) {
 this.outputFileName = outputFileName;
 }

public void setScreenshotName(String screenshotName) {
 this.screenshotName = screenshotName;
 }

@Test
 public void runTest() throws Exception {
 Connection conn = null;
 try {
 Class.forName("com.mysql.jdbc.Driver");
 conn = DriverManager
 .getConnection(
 "jdbc:mysql://connection_string/database_name",
 "user_name", "password");
 } catch (SQLException e) {
 e.printStackTrace();
 } catch (ClassNotFoundException e) {
 e.printStackTrace();
 }
 List tables = new ArrayList();
 System.out.println("The lenth of the two dimensional array is "
 + pageFunctionalities.length);
 for (int i = 0; i < pageFunctionalities.length; i++) {
 ResultSet resultSet = null;
 System.out.println("ID: " + pageFunctionalities[i][0] + " Functionality: "
 + pageFunctionalities[i][1]);
 PreparedStatement preparedStatement = conn
 .prepareStatement("select TableForFunctionality from auto_tabData where Id = '"
 + pageFunctionalities[i][0]
 + "' and Functionality = '"
 + pageFunctionalities[i][1] + "'");
 resultSet = preparedStatement.executeQuery();
 System.out.println("Query Execution Count: " + i);
 while (resultSet.next()) {
 tables.add(resultSet.getString(1));
 }
 }
 System.out.println("The tables to be executed are as below");
 for (String val : tables) {
 System.out.println(val);
 }
 TestConfig testConfig = new TestConfig();
 testConfig.setBaseUrl(baseURL);
 testConfig.setOutputFileName(outputFileName);
 testConfig.setScreenshotName(screenshotName);
 testConfig.setUp();
 for (String val : tables) {
 System.out.println("Table being executed: " + val);
 testConfig.setTableName(val);
 testConfig.testElements();
 }
 testConfig.tearDown();
 }
}

Now comes the 3rd type of class. Let’s name it ‘TestConfig‘ which looks as below.

import com.pilanisoftlabs.TestElements.*;
import com.pilanisoftlabs.TestElements.Image;
import java.util.concurrent.TimeUnit;
import jxl.format.*;
import jxl.format.CellFormat;
import jxl.format.Colour;
import jxl.write.*;
import static org.junit.Assert.*;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.File;
import jxl.*;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import java.sql.*;

@SuppressWarnings("unused")
public class TestConfig {
 private WebDriver driver;
 private String baseUrl = "http://www.google.com/";
 private StringBuffer verificationErrors = new StringBuffer();
 private int counter = 1;
 private boolean result;
 private String actualResult;
 private WritableWorkbook workbook;
 private WritableSheet sheet;
 private int rowCounter = 4;
 private Integer passCounter = 0;
 private Integer failCounter = 0;
 private String tableName = "";
 private String outputFileName = "";
 private String screenshotName = "";

public void setBaseUrl(String baseUrl) {
 this.baseUrl = baseUrl;
 }

public void setTableName(String tableName) {
 this.tableName = tableName;
 }

public void setOutputFileName(String outputFileName) {
 this.outputFileName = outputFileName;
 }

public void setScreenshotName(String screenshotName) {
 this.screenshotName = screenshotName;
 }

@BeforeClass
 public void setUp() throws Exception {
 driver = new FirefoxDriver();
 driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
 driver.get(baseUrl);

 workbook = Workbook.createWorkbook(new File("test-output/"
 + outputFileName + ".xls"));
 sheet = workbook.createSheet("output", 0);
 WritableFont writableFont = new WritableFont(WritableFont.ARIAL, 10,
 WritableFont.BOLD);
 WritableCellFormat writableCellFormat = new WritableCellFormat(
 writableFont);

 Label l1 = new Label(0, 3, "Name", writableCellFormat);
 Label l2 = new Label(1, 3, "Expected Result", writableCellFormat);
 Label l3 = new Label(2, 3, "Actual Result", writableCellFormat);
 Label l4 = new Label(3, 3, "Cause", writableCellFormat);
 Label l5 = new Label(4, 3, "Status", writableCellFormat);
 sheet.addCell(l1);
 sheet.addCell(l2);
 sheet.addCell(l3);
 sheet.addCell(l4);
 sheet.addCell(l5);
 }

@Test()
 public void testElements() throws Exception {
 Connection conn = null;
 try {
 Class.forName("com.mysql.jdbc.Driver");
 conn = DriverManager
 .getConnection(
 "jdbc:mysql://connection_string/database_name",
 "user_name", "password");
 } catch (SQLException e) {
 e.printStackTrace();
 } catch (ClassNotFoundException e) {
 e.printStackTrace();
 }
 ResultSet resultSet = null;
 PreparedStatement preparedStatement = conn
 .prepareStatement("select * from " + tableName);
 resultSet = preparedStatement.executeQuery();

 while (resultSet.next()) {
 String property = resultSet.getString(1);
 String locator = resultSet.getString(2);
 String locatorType = resultSet.getString(3);
 String value = resultSet.getString(4);
 String expectedResult = resultSet.getString(5);
 String permission = resultSet.getString(6);
 String name = resultSet.getString(7);

 System.out.println("Current Row is " + property + ", " + locator
 + ", " + locatorType + ", " + value + ", " + expectedResult
 + ", " + permission + ", " + name);

 // Start of Page Title testing block
 if (property.equals("pagetitle")) {
 PageTitle title = new PageTitle(driver, property, locator,
 locatorType, value, expectedResult, permission, name);
 result = title.testPageTitle();
 actualResult = title.getActualResult();
 updateResult(result, name, expectedResult, title.getCause());
 }
 // End of Page Title testing block

 // Start of Link testing block, used to click on links and get title
 // tag of links
 if (property.equals("link")) {
 Link link = new Link(driver, property, locator, locatorType,
 value, expectedResult, permission, name);
 result = link.testLink();
 actualResult = link.getActualResult();
 updateResult(result, name, expectedResult, link.getCause());
 String fileLocation = "test-output/Screenshot/"
 + screenshotName + counter + ".png";
 ScreenShot ss = new ScreenShot(driver, fileLocation);
 ss.takeScreenshot();
 counter++;
 }
 // End of Link testing block

 // Start of Image testing block used to click on images and to test
 // alttag for them
 if (property.equals("image")) {
 Image image = new Image(driver, property, locator, locatorType,
 value, expectedResult, permission, name);
 result = image.testImage();
 actualResult = image.getActualResult();
 updateResult(result, name, expectedResult, image.getCause());
 }
 // End of Image testing block

 // Start of Dropdown testing block, used to select a value from
 // dropdown, to find out number of elements and to show all the
 // elements
 if (property.equals("dropdown")) {
 DropDown dropDown = new DropDown(driver, property, locator,
 locatorType, value, expectedResult, permission, name);
 result = dropDown.testDropDown();
 actualResult = dropDown.getActualResult();
 updateResult(result, name, expectedResult, dropDown.getCause());
 }
 // End of Dropdown testing block

 // Start of Text Box testing block, used to test if text box is
 // editable, to write values in them
 if (property.equals("textbox")) {
 TextBox textBox = new TextBox(driver, property, locator, locatorType, value, expectedResult,
 permission, name);
 result = textBox.testTextBox();
 actualResult = textBox.getActualResult();
 updateResult(result, name, expectedResult, textBox.getCause());
 }
 // End of Text Box testing block

 // Start of Radio Button testing block, used to test if radio button
 // is clickable, to click a particual radio button
 if (property.equals("radio")) {
 Radio radio = new Radio(driver, property, locator, locatorType,
 value, expectedResult, permission, name);
 result = radio.testRadio();
 actualResult = radio.getActualResult();
 updateResult(result, name, expectedResult, radio.getCause());
 }
 // End of Radio Button testing block

 // Start of Check Box testing block, used to test if check box is
 // clickable, to select some check boxes
 if (property.equals("checkbox")) {
 CheckBox checkBox = new CheckBox(driver, property, locator,
 locatorType, value, expectedResult, permission, name);
 result = checkBox.testCheckBox();
 actualResult = checkBox.getActualResult();
 updateResult(result, name, expectedResult, checkBox.getCause());
 }
 // End of Check Box testing block

 // Start of text checking block, used to test presence of specific
 // text in a page
 if (property.equals("text")) {
 Text text = new Text(driver, property, locator, locatorType,
 value, expectedResult, permission, name);
 result = text.testText();
 actualResult = text.getActualResult();
 updateResult(result, name, expectedResult, text.getCause());
 }
 // End of text checking block

 // Start of URL testing block, used to get the URL being displayed
 // for a page
 if (property.equals("url")) {
 Url url = new Url(driver, property, locator, locatorType,
 value, expectedResult, permission, name);
 url.setBaseURL(baseUrl);
 result = url.testUrl();
 actualResult = url.getActualResult();
 updateResult(result, name, expectedResult, url.getCause());
 }
 // End of URL testing block

 // Start of value testing block used to test the value present in
 // any object
 if (property.equals("value")) {
 ValueTest valueTest = new ValueTest(driver, property, locator,
 locatorType, value, expectedResult, permission, name);
 result = valueTest.testValue();
 actualResult = valueTest.getActualResult();
 updateResult(result, name, expectedResult, valueTest.getCause());
 }
 // End of value testing block

 // Start of Object finder block
 if (property.equals("object")) {
 ObjectFinder objectFinder = new ObjectFinder(driver, property,
 locator, locatorType, value, expectedResult,
 permission, name);
 result = objectFinder.testObjectPresent();
 actualResult = objectFinder.getActualResult();
 updateResult(result, name, expectedResult,
 objectFinder.getCause());
 }
 // End of object finder block

 if (property.equals("pop up window")) {
 NewWindow newWindow = new NewWindow(driver, property, locator,
 locatorType, value, expectedResult, permission, name);
 result = newWindow.testNewWindow();
 actualResult = newWindow.getActualResult();
 updateResult(result, name, expectedResult, newWindow.getCause());
 }
 }

}

@AfterClass
 public void tearDown() throws Exception {
 WritableFont writableFont = new WritableFont(WritableFont.ARIAL, 10,
 WritableFont.BOLD);
 WritableCellFormat writableCellFormat = new WritableCellFormat(
 writableFont);
 Label l1 = new Label(0, 0, "Steps Passed", writableCellFormat);
 Label l2 = new Label(1, 0, passCounter.toString(), writableCellFormat);
 sheet.addCell(l1);
 sheet.addCell(l2);
 Label l3 = new Label(0, 1, "Steps Failed", writableCellFormat);
 Label l4 = new Label(1, 1, failCounter.toString(), writableCellFormat);
 sheet.addCell(l3);
 sheet.addCell(l4);
 workbook.write();
 workbook.close();
 System.out.println("Closed the file ...");
 driver.quit();
 String verificationErrorString = verificationErrors.toString();
 if (!"".equals(verificationErrorString)) {
 fail(verificationErrorString);
 }
 }

private boolean isElementPresent(By by) {
 try {
 driver.findElement(by);
 return true;
 } catch (NoSuchElementException e) {
 return false;
 }
 }

private void updateResult(boolean bool, String name, String expectedResult,
 String cause) throws Exception {
 Label label1 = new Label(0, rowCounter, name);
 Label label2 = new Label(1, rowCounter, expectedResult);
 Label label3 = new Label(2, rowCounter, actualResult);
 Label label4 = new Label(3, rowCounter, cause);
 sheet.addCell(label1);
 sheet.addCell(label2);
 sheet.addCell(label3);
 sheet.addCell(label4);
 if (result) {
 WritableFont writableFont = new WritableFont(WritableFont.ARIAL,
 10, WritableFont.BOLD);
 WritableCellFormat writableCellFormat = new WritableCellFormat(
 writableFont);
 writableCellFormat.setBackground(Colour.GREEN);
 Label label5 = new Label(4, rowCounter, "Pass", writableCellFormat);
 passCounter++;
 sheet.addCell(label5);
 } else {
 WritableFont writableFont = new WritableFont(WritableFont.ARIAL,
 10, WritableFont.BOLD);
 WritableCellFormat writableCellFormat = new WritableCellFormat(
 writableFont);
 writableCellFormat.setBackground(Colour.RED);
 Label label5 = new Label(4, rowCounter, "Fail", writableCellFormat);
 failCounter++;
 sheet.addCell(label5);
 }
 rowCounter++;
 }

}

Rest of this post, I would be explaining the next part. There we’ll concentrate more on the 3rd type of the class. Stay tuned…. 🙂

5 thoughts on “A Better Way of Writing Selenium Scripts [Part – 1]

Leave a comment