Monday, October 31, 2011

DataProvider - Data Driven Testing with Selenium and TestNG



Data-Driven testing generally means executing a set of steps with multiple sets of data.
Selenium does not provide any out-of-the box solution for data driven testing but leaves it up to the user to implement this on his own. People who are familiar with QuickTest Professional or QTP would know Datatables (Global & Action) that allows the scripter to link a action or a function with multiple rows of data.

TestNG is a framework that makes data-driven testing possible in selenium. TestNG is a testing framework created in line with Junit but with added features that makes it suitable for use in regression test automation projects. DataProvider is one such feature in testng; it allows a test method to be executed with multiple sets of data.

A DataProvider is data feeder method defined in your class that supplies a test method with data. You may hook up any test method with a DataProvider and make the test method execute once for every row of your test data.
@DataProvider(name = "DP1")
    public Object[][] createData() {
        Object[][] retObjArr={{"001","Jack","London"},
                            {"002","John","New York"},
                            {"003","Mary","Miami"},
{"004","George","california"}};
        return(retObjArr);
    }

  
@Test (dataProvider = "DP1")
    public void testEmployeeData(String empid, String empName, String city){
selenium.type("id", empid);
selenium.type("name", empName);
selenium.click("submit_button");
selenium.waitForPageToLoad("30000");
assertTrue(selenium.isTextPresent(city));

}


In the example above createData() is the DataProvider function that is sending out test data in the form of array of array of objects (object[][]). The test method testEmployeeData() hooks onto the DataProvider by declaring that its data should be supplied by the DataProvider named "DP1". On execution testEmployeeData() will be executed 4 times, once with each row of object[][]. Note that the dimension of the array returned by the DataProvider must match the parameter type of the test method. This means if your test method takes 3 parameters then array returned by the DataProvider must have its second dimension equal to 3. Try to visualize the array of array as a table where each row constitutes one test case.

In the example above the test data is defined withing the DataProvider function. But ideally you want to keep your test data in spreadsheet like media and not in the test script. To achieve this instead of declaring the test data array within the DataProvider function we should call a function that fetches the data from an excel sheet and returns an array of array object to the DataProvider.
I will share with you my way of storing data in XL sheets. I store test data in the form of tables within an excel sheet. There could be any number of tables within an excel sheet. Each table is marked by an startTag and an endTag. The startTag and endTag is the Name of the table. The startTag is placed one cell diagonally above the cell from where the first row of table data begins. And the endTag is placed one cell diagonally below the cell from where last row of table data ends. A table always begins with a column header row although this row is just for our understanding and is ignored in the computation. Below is the image of a sample test data excel sheet. Click on the image to see a bigger version of it. Also below the image you can see the google doc with the test data I will use in an example later in this post.
https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWu21YPCJO1LbIP76RrPs1InjFRCxBn9DslC2oOaSznAwXb9Fcn1bzm91ARbIK5W4bwnVb-ad88hF4-puy6UWPh_TyHY6Q4y-4qBMCQJppBt4y-e2yuotZfe0L92hA2hUESjCl5Z8KT-U/s400/dataProviderTestData3.PNG

Below is the function that fetches the table from the excel sheet and returns it as an array of array of String type. It uses Java Excel API to fetch data from excel sheet.
It takes 3 parameters
1. xlFilePath - the path of XL file/workbook containing the data, the path is relative to java project
2. sheetName- name of the xl sheet that contains the table
3. tableName- Name of the table that you wish to fetch
The function returns a String type array of array.


public String[][] getTableArray(String xlFilePath, String sheetName, String tableName){
        String[][] tabArray=null;
        try{
            Workbook workbook = Workbook.getWorkbook(new File(xlFilePath));
            Sheet sheet = workbook.getSheet(sheetName);
            int startRow,startCol, endRow, endCol,ci,cj;
            Cell tableStart=sheet.findCell(tableName);
            startRow=tableStart.getRow();
            startCol=tableStart.getColumn();

            Cell tableEnd= sheet.findCell(tableName, startCol+1,startRow+1, 100, 64000,  false);                              

            endRow=tableEnd.getRow();
            endCol=tableEnd.getColumn();
            System.out.println("startRow="+startRow+", endRow="+endRow+", " +
                    "startCol="+startCol+", endCol="+endCol);
            tabArray=new String[endRow-startRow-1][endCol-startCol-1];
            ci=0;

            for (int i=startRow+1;i<endRow;i++,ci++){
                cj=0;
                for (int j=startCol+1;j<endCol;j++,cj++){
                    tabArray[ci][cj]=sheet.getCell(j,i).getContents();
                }
            }
        }
        catch (Exception e)    {
            System.out.println("error in getTableArray()");
        }

        return(tabArray);
    }


Now we will put all of this together in an example. We will keep the test data in excel sheets and use the above function to fetch from it.
Note:
1. testng jar should be in the class path. You may download the latest testng jar file from here.
2. testng eclipse plugin must be installed. Get it from the same location as the testng jar file mentioned above.
3. jxl jar should be in the class path Java Excel API. You can get this from here. Download the jexcelapi.zip, unzip to get the jxl.jar
4. Selenium-server and selenium-java-client-driver and junit jar (selenium classes need junit) files must in class path.
5. The file data1.xls is kept at test\\Resources\\Data\\data1.xls
I have attached this file to the blog as a google doc, please copy paste its contents to a excel file. Rename the excel-sheet to DataPool and the save the excel file as data.xls. The path mentioned is relative to the project.

The test method testDataProviderExample() below navigates to www.imdb.com and performs search for movie titles. On the movie details page it verifies the presence of director name, movie plot and the name of the lead actor. This routine is repeated for all the rows of the test-data table.


import com.thoughtworks.selenium.*;

import org.junit.AfterClass;
import org.openqa.selenium.server.SeleniumServer;
import org.testng.annotations.*;

import java.io.File;
import jxl.*;


public class DataProviderExample extends SeleneseTestCase{
   
    @BeforeClass
    public void setUp() throws Exception {
        SeleniumServer seleniumserver=new SeleniumServer();
        seleniumserver.boot();
        seleniumserver.start();
        setUp("http://www.imdb.com/", "*firefox");
        selenium.open("/");
        selenium.windowMaximize();
        selenium.windowFocus();
    }

   
    @DataProvider(name = "DP1")
    public Object[][] createData1() throws Exception{
        Object[][] retObjArr=getTableArray("test\\Resources\\Data\\data1.xls",
                "DataPool", "imdbTestData1");
        return(retObjArr);
    }
   
    @Test (dataProvider = "DP1")
    public void testDataProviderExample(String movieTitle,
            String directorName, String moviePlot, String actorName) throws Exception {   
        //enter the movie title
        selenium.type("q", movieTitle);
        //they keep switching the go button to keep the bots away
        if (selenium.isElementPresent("nb15go_image"))
            selenium.click("nb15go_image");
        else
        selenium.click("xpath=/descendant::button[@type='submit']");
       
        selenium.waitForPageToLoad("30000");
        //click on the movie title in the search result page
        selenium.click("xpath=/descendant::a[text()='"+movieTitle+"']");
        selenium.waitForPageToLoad("30000");
        //verify director name is present in the movie details page
        verifyTrue(selenium.isTextPresent(directorName));
        //verify movie plot is present in the movie details page
        verifyTrue(selenium.isTextPresent(moviePlot));
        //verify movie actor name is present in the movie details page
        verifyTrue(selenium.isTextPresent(actorName));
    }
   
    @AfterClass
    public void tearDown(){
        selenium.close();
        selenium.stop();
    }
   
    public String[][] getTableArray(String xlFilePath, String sheetName, String tableName) throws Exception{
        String[][] tabArray=null;
       
            Workbook workbook = Workbook.getWorkbook(new File(xlFilePath));
            Sheet sheet = workbook.getSheet(sheetName);
            int startRow,startCol, endRow, endCol,ci,cj;
            Cell tableStart=sheet.findCell(tableName);
            startRow=tableStart.getRow();
            startCol=tableStart.getColumn();

            Cell tableEnd= sheet.findCell(tableName, startCol+1,startRow+1, 100, 64000,  false);               

            endRow=tableEnd.getRow();
            endCol=tableEnd.getColumn();
            System.out.println("startRow="+startRow+", endRow="+endRow+", " +
                    "startCol="+startCol+", endCol="+endCol);
            tabArray=new String[endRow-startRow-1][endCol-startCol-1];
            ci=0;

            for (int i=startRow+1;i<endRow;i++,ci++){
                cj=0;
                for (int j=startCol+1;j<endCol;j++,cj++){
                    tabArray[ci][cj]=sheet.getCell(j,i).getContents();
                }
            }
       

        return(tabArray);
    }
   
   
}//end of class


Just copy paste the above in a java file, reference the necessary jars in the build path, put the test-data excel file in the correct location and it should work just fine. To run right click the java file in eclipse and selectRun As-->TestNG Test.

Extending the Default Selenium or What the heck is Default Selenium?

Extending the Default Selenium or What the heck is Default Selenium?, Selenium Notes, Videos, Examples By.www.laxmiroy.blogspot.com/


Calm down!, DefaultSelenium is the base class that you instantiate early on in your test class to get the selenium object which you later use in almost every line of you test script.

.
.
Selenium selenium=new DefaultSelenium("localhost",4444,"*chrome","http://www.google.com/");    
selenium.start();
selenium.open("/");
.
.
.
                  OR              

/*Creates a new DefaultSelenium object and starts it using the specified baseUrl and browser string*/                       
 setUp("http://www.imdb.com/", "*firefox");
selenium.open("/");
selenium.click("nb15go_image");
.
.
.
does that ring a bell? 

What do you mean by extending the default selenium?

What do you mean by extending the default selenium? Selenium Notes, Videos, Examples By.www.laxmiroy.blogspot.com/


Extending the default selenium means enhancing or customizing or modifying the existing features of selenium class. We are reusing the existing class after adding some enhancements so as to customize the existing features to our taste and requirement. Programming languages such as JAVA provide the facility of extending/modifying the behavior of a class without having to re-write the entire class. We can create a new class that inherits the attributes and methods of another. We don't need a copy of the original source code (as is the case with many other languages) to extend the usefulness of a class/library.

Why do I need to extend selenium?

Why do I need to extend selenium? Selenium Notes, Videos, Examples By.www.laxmiroy.blogspot.com/


If you want certain operations to be done every time before a step is executed then you would have to extend the default selenium. For example let’s say that you wish to wait 5 seconds after every step or you want to highlight the element before clicking on it or you want to record every step in log file or you want to poll for a element’s existence for a certain amount of time before performing an operation (click, type) on it. For all these to happen you would have to extend the default selenium class that you use in your RC test script.

Below is a simple example that demonstrates this concept. The classMySelenium extends the class DefaultSelenium. The methods click andtype of the base class have been over-ridden in such a way that the target element (which is to be clicked on or typed into) is highlighted thrice before the click or type operation. The class UsingMySelenium by the means of a simple example demonstrates the usage of our extended class.

Class MySelenium

package seleniumExtention;

import com.thoughtworks.selenium.DefaultSelenium;

public class MySelenium  extends DefaultSelenium{

public MySelenium(String seleniumSeverHost, int port,String browserString,String autURL){       
super(seleniumSeverHost, port,browserString, autURL); 
}

@Override
public void click(String target) {
try {
for (int i=0;i<3;i++){
super.highlight(target); 
Thread.sleep(500);
}

super.click(target);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@Override
public void type(String target, String value){
try{
for (int i=0;i<3;i++){
super.highlight(target);
Thread.sleep(500);
}
super.type(target,value);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}



Class UsingMySelenium

package seleniumExtention;

import org.junit.Test;
import org.junit.BeforeClass;
import org.openqa.selenium.server.SeleniumServer;

import com.thoughtworks.selenium.SeleneseTestCase;

public class UsingMySelenium extends SeleneseTestCase{
public MySelenium selenium;

@BeforeClass
public void setUp() throws Exception {

SeleniumServer seleniumserver=new SeleniumServer();
seleniumserver.boot();
seleniumserver.start();

/*using the customized selenium class that we created earlier instead of the default selenium*/ 
selenium=new MySelenium ("localhost",4444,"*iexplore","http://www.google.com/");
selenium.start();
selenium.windowMaximize();

}

@Test
public void testSimpleGoogleSearch() throws Exception {
selenium.open("/");
selenium.waitForPageToLoad("5000");
selenium.type("q", "Aston Martin volante");
selenium.click("//input[@value='Google Search']");
selenium.waitForPageToLoad("5000");
selenium.click("xpath=/descendant::a[text()=' - The Cars']/em[text()='Aston Martin']/parent::a");  
selenium.waitForPageToLoad("25000");
Thread.sleep(5000);
}
}



To make this work you need to do the following
1) Create a package seleniumExtention inside the java project.
2) Create the classes MySelenium & UsingMySelenium and copy paste the content as given above.
3) Run the testSimpleGoogleSearch() given in the classUsingMySelenium (right click Run As-->JUnit Test).

*Following jars need to be referenced
selenium-server.jar
selenium-java-client-driver.jar
junit.jar

Maximize browser window using java robot in Selenium RC

Maximize browser window using java robot in Selenium RC, Selenium Notes, Videos, Examples By.www.laxmiroy.blogspot.com/


Have you noticed that selenium RC does not maximize the browser window even after invoking the selenium.windowMaximize( ) ? The window is not stretched-out fully.


The only way to maximize the browser window fully I suppose is to use the java robot to do a keyboard simulation of ALT+SPACE which displays the main window's system menu and then using the down arrow key reach the maximize option and then do an ENTER to invoke this option.

Below is the code that will do this for you.
//maximize the window as much a possible using selenium

  selenium.windowMaximize();

//bring the browser window into focus

  selenium.windowFocus();

//invoke the system menu using robot 

  Robot robot = new Robot();
  robot.keyPress(KeyEvent.VK_ALT);
  robot.keyPress(KeyEvent.VK_SPACE);
  robot.keyRelease(KeyEvent.VK_ALT);
  robot.keyRelease(KeyEvent.VK_SPACE);
  Thread.sleep(1000);

//Press down arrow key to move down the menu

       robot.keyPress(KeyEvent.VK_DOWN);
  robot.keyRelease(KeyEvent.VK_DOWN);
  Thread.sleep(100);

  robot.keyPress(KeyEvent.VK_DOWN);
  robot.keyRelease(KeyEvent.VK_DOWN);
  Thread.sleep(100);

  robot.keyPress(KeyEvent.VK_DOWN);
  robot.keyRelease(KeyEvent.VK_DOWN);
  Thread.sleep(100);

  robot.keyPress(KeyEvent.VK_DOWN);
  robot.keyRelease(KeyEvent.VK_DOWN);
  Thread.sleep(100);

//Press enter to invoke the Maximize menu option  

       robot.keyPress(KeyEvent.VK_ENTER);
  robot.keyRelease(KeyEvent.VK_ENTER);

Don't forget to import the following in your test class.


import java.awt.Robot;
import java.awt.event.KeyEvent;

no need to ad anything to environment variable. try this

SeleniumServer seleniumserver=new SeleniumServer();
seleniumserver.boot();
seleniumserver.start();
setUp("http://www.imdb.com/", "*firefox");
selenium.open("/");
selenium.windowMaximize();
selenium.windowFocus();

Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_ALT);
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_ALT);
robot.keyRelease(KeyEvent.VK_SPACE);
Thread.sleep(1000);

//Press down arrow key to move down the menu

robot.keyPress(KeyEvent.VK_DOWN);
robot.keyRelease(KeyEvent.VK_DOWN);
Thread.sleep(100);

robot.keyPress(KeyEvent.VK_DOWN);
robot.keyRelease(KeyEvent.VK_DOWN);
Thread.sleep(100);

robot.keyPress(KeyEvent.VK_DOWN);
robot.keyRelease(KeyEvent.VK_DOWN);
Thread.sleep(100);

robot.keyPress(KeyEvent.VK_DOWN);
robot.keyRelease(KeyEvent.VK_DOWN);
Thread.sleep(100);

//Press enter to invoke the Maximize menu option

robot.keyPress(KeyEvent.VK_ENTER);
robot.keyRelease(KeyEvent.VK_ENTER);

Using UI-Elements user-extension with Selenium RC

Using UI-Elements user-extension with Selenium RC, Selenium Notes, Videos, Examples By.www.laxmiroy.blogspot.com/


Please refer to my post UI-Elements - All about it to understand what UI-Elements are. In this post I wish to describe how UI-Elements can be used with Selenium RC.

There are a few things that are different here from how it is used with selenium ide
1) In Selenium IDE the UI-Map file is added to the IDE environment as selenium core extension. You may add any number of such files from the IDE->options->Selenium core extensions.
In Selenium RC the UI Map file is added either programatically or added as command line parameter when invoking selenium-server.

2)In Selenium IDE you may add as many map files as you wish. However in
Selenium RC you can add only one UI-Map file and that file must be in the same directory as selenium-server.jar. Also the name of ui-map file must be 'user-extensions.js'.

Below is code for attaching the ui-map file to the selenium server. The 'getConfiguration()' method of seleniumserver object is used to get the configuration object and the 'setUserExtension()' method is used to set the uimap file as the user-extension.
.
.
.
SeleniumServer seleniumserver;
seleniumserver=new SeleniumServer();
RemoteControlConfiguration a= seleniumserver.getConfiguration();
File uimap=new File("lib\\user-extensions.js");
a.setUserExtensions(uimap);
seleniumserver.boot();
seleniumserver.start();
.
.
.

Now what if you have multiple ui-map files? In that case you must combine all of them together to create one consolidated 'user-extension.js' file. I have written a function that does this for me.
Please note that you must remove the instruction


var myMap = new UIMap();

from the individual map files as the function adds it automatically in the combined file.

public static void createUIMAP(String masterUIMAPfilePath) throws java.io.IOException{             
RandomAccessFile file = null;
RandomAccessFile fileui = null;
String line = null;
String lineui = null;


File oldFile=new File("lib\\user-extentions.js");
if (oldFile.exists())
oldFile.delete();

FileWriter fstream = new FileWriter("lib\\user-extensions.js",true);
String theFirstLine="var myMap = new UIMap();";
fstream.write(theFirstLine, 0, theFirstLine.length());
file = new RandomAccessFile(masterUIMAPfilePath, "r");
while ((line = file.readLine()) != null) {
System.out.println(line);
fileui = new RandomAccessFile(line, "r");
while ((lineui = fileui.readLine()) != null) {
System.out.println(lineui);
fstream.write(lineui, 0, lineui.length());
fstream.write(System.getProperty("line.separator"));

}
}
fstream.close();
}
This function deletes any file with the name 'user-extension.js' that may be there in the \lib folder within your java project and creates a new 'user-extension.js' file by combining any number of child ui-map file(s) whose location(s) is mentioned in the text file masterUIMAPfile whose path is supplied as a parameter to this function.
The masterUIMapFile may look something like this (all paths are relative to the java project).

test\\Resources\\UIMaps\\admin\\uimap1.js
test\\Resources\\UIMaps\\admin\\uiMapGoogle.js
I have placed the 'uiMaster.txt' intest\Resources\UIMaps\admin\uiMaster.txt within my java project.
Now I will put this all together with the script that was created in my lastpost. The createUIMAP() function is to be called before calling the setUserExtensions() method.


import org.junit.AfterClass;
import org.junit.Test;
import org.openqa.selenium.server.RemoteControlConfiguration;
import org.openqa.selenium.server.SeleniumServer;
import com.thoughtworks.selenium.*;
import java.io.File;
import java.io.FileWriter;
import java.io.RandomAccessFile;


public class testGoogleUIMapNew extends SeleneseTestCase {
public void setUp() throws Exception {
createUIMAP("test\\Resources\\UIMaps\\admin\\uiMaster.txt");
SeleniumServer seleniumserver;
seleniumserver=new SeleniumServer();
RemoteControlConfiguration a= seleniumserver.getConfiguration();
File uimap=new File("lib\\user-extensions.js");
a.setUserExtensions(uimap);
seleniumserver.boot();
seleniumserver.start();

setUp("http://www.google.com/", "*firefox");
}



@Test public void testGoogleuimap() throws Exception {
selenium.open("www.google.co.in");
verifyTrue(selenium.isElementPresent("ui=googleSearch_page::theBigGoogleLogo()"));
selenium.type("ui=googleSearch_page::search_Textbox()", "wikipedia");
selenium.click("ui=googleSearch_page::search_button()");
selenium.waitForPageToLoad("30000");
verifyTrue(selenium.isElementPresent("ui=googleSearch_page::searchwikipedia_textbox()"));
selenium.type("ui=googleSearch_page::searchwikipedia_textbox()", "selenium");
selenium.click("ui=googleSearch_page::searchwikipedia_button()");
selenium.waitForPageToLoad("30000");
verifyTrue(selenium.isElementPresent("ui=googleSearch_page::seleniumSearchResult_Link()")); 
selenium.click("ui=googleSearch_page::seleniumSearchResult_Link()");
selenium.waitForPageToLoad("30000");
verifyEquals(selenium.getTitle(), "Selenium (software) - Wikipedia, the free encyclopedia");
verifyTrue(selenium.isElementPresent("ui=wikipediaArticle_page::wikipediaGlobe_image()"));              
}


@AfterClass
public void tearDown(){
selenium.close();
selenium.stop();
} //end of tearDown()

public static void createUIMAP(String masterUIMAPfilePath) throws java.io.IOException{
RandomAccessFile file = null;
RandomAccessFile fileui = null;
String line = null;
String lineui = null;


File oldFile=new File("lib\\user-extentions.js");
if (oldFile.exists())
oldFile.delete();

FileWriter fstream = new FileWriter("lib\\user-extensions.js",true);
String theFirstLine="var myMap = new UIMap();";
fstream.write(theFirstLine, 0, theFirstLine.length());   
file = new RandomAccessFile(masterUIMAPfilePath, "r");
while ((line = file.readLine()) != null) {
System.out.println(line);
fileui = new RandomAccessFile(line, "r");
while ((lineui = fileui.readLine()) != null) {
System.out.println(lineui);
fstream.write(lineui, 0, lineui.length());
fstream.write(System.getProperty("line.separator"));

}
}
fstream.close();
}
}

To run this just right click and choose option 'Run As junit test'. Please remember that in my case the 'selenium-server.jar' fie is in the /lib folder.

You should ideally tuck away the createUIMAP() function and the code to start the selenium-server in a utility class in your project and just be calling them in your test script.

Also note that the testGoogleuimap() test method in this example is from my last post. You can derive this java equivalent of the IDE recording by doing Options-->format-->Java (Junit) - Selenium RCfrom Selenium IDE. I have just replaced the awfully big while constructs in the script with

selenium.waitForPageToLoad("30000");
As always junit, selenium-server and selenium-client-driver are in the classpath.
UI-Elements - All about it!