Selenium Tutorials

Data provider and TestNG XML: Parameterization in Selenium

When we create any software, we create in a way that it should work differently with a different set of data. When it comes to testing the same created software, we can’t test it just with one set of data. Here, we need to verify our created software with all set of combinations which are expected to support. Here Parameterization comes into the picture. We need to parameterize our test scripts to pass multiple data to the application at runtime. The concept which achieves by parameterization is called DataDriven Testing.

Type of Parameterization in TestNG  

To get more clear on parameterization, we will go through the parameterization options in TestNG framework using Selenium WebDriver.

There are two ways in TestNG to achieve parameterization

  1. With the help of Parameters annotation and TestNG XML file.
parameters.PNG
  1. With the help of DataProvider annotation.
searchkey.PNG

Parameters from Testng.xml can be suite level or test level

Parameter from DataProvider can take Method and ITestContext as a parameter.

Parameters annotation with Testng.xml

Select parameterization using annotations when you deal with complexity and the number of input combinations is less.

Let consider the below scenario to Open google.com in your browser and search 3 keywords separately. To complete the scenario, below are the steps that our script has to perform.

Step 1: Launch the browser and go to the Google.com site

Step 2: Enter a search Keyword as input in the search box and click search.

Step 3: Verify the input value on UI to be the same as test data.

Step 4: Repeat the 2 and 3 steps for the other 2 keywords.

KEYWORD TO SEARCH
Selenium
JMeter
QTP

The below code shows how we can do it without parameterization in TestNG.

package parameterization;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
//import org.testng.Assert;
import org.testng.AssertJUnit;
import org.testng.annotations.Test;

public class WithoutParameter {
String driverPath = "F:\\drivers\\geckodriver.exe";
WebDriver driver;
    
    @Test
    public void testNoParameter() throws InterruptedException{
        String author = "test";
        String keyWord = "Selenium";
        
        System.setProperty("webdriver.gecko.driver", driverPath);        
        driver= new FirefoxDriver();
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        
        driver.get("https://google.com");
        WebElement searchText = driver.findElement(By.name("q"));
        //Searching text in google text box
        searchText.sendKeys(keyWord);
        
        System.out.println("Greeting ->"+author+" The Search key is->"+keyWord);
                System.out.println("Thread will sleep now");
        
        Thread.sleep(3000);
        System.out.println("Value in Google Search Box = "+searchText.getAttribute("value") +" ::: Value given by input = "+keyWord);
        //verifying the value in google search box
        AssertJUnit.assertTrue(searchText.getAttribute("value").equalsIgnoreCase(keyWord));
}
}

To pass the other 2 keywords, we’ll have to write the same piece of code again with different keyword values for the string ‘keyWord’, which would result in a lengthy and repetitive code.

Let us now simplify our problem bypassing these keywords as parameters in our testng.xml and adding @parameters annotation in our test.

Java Code:

package parameterization;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
//import org.testng.Assert;
import org.testng.AssertJUnit;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

public class ParameterizedClass {
String driverPath = "F:\\drivers\\geckodriver.exe";
WebDriver driver;
    @Test
    @Parameters({"author","keyWord"})
    public void testParameterizationWithXML( @Optional("Abc") String author,String keyWord) throws InterruptedException{

        System.setProperty("webdriver.gecko.driver", driverPath);
        driver = new FirefoxDriver();
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        driver.get("https://google.com");

        WebElement searchText = driver.findElement(By.name("q"));
        //Searching text in google text box
        searchText.sendKeys(keyWord);

        System.out.println("Greeting ->"+author+" The search key is->"+keyWord);
        System.out.println("Thread will sleep now");
        Thread.sleep(3000);
        System.out.println("Value in Google Search Box = "+searchText.getAttribute("value") +" ::: Value given by input = "+keyWord);
        //verifying the value in google search box
        AssertJUnit.assertTrue(searchText.getAttribute("value").equalsIgnoreCase(keyWord));

}
}

Below is the TestNG.xml which we used to perform parameterization in TestNG for Selenium automation testing.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="TestSuite" thread-count="3" >
<parameter name="author" value="test" />
<parameter name="KeyWord" value="Selenium" />
<test name="test">

<classes>
<class name="parameterization.ParameterizedClass">
</class>
</classes>
</test>
</suite>

We follow below instructions to run the script by selecting the XML file and Run as Test NG SuiteRight Click on testng.xml file -> Run as -> Testng Suite (Note : Suite)

parametertestng.PNG

Now, parameters can be defined at 2 levels

  1. Suite level – The parameters inside the <suite> tag of TestNG XML file will be a suite level parameter.
  2. Test Level — The parameters inside the <Test> tag of testing XML file will be a Test level parameter.

Here is a similar test with suite level parameters

thread1.PNG

NOTE: In the case, if the parameter name is the same in the suite level and test level then the test level parameter will get preference over the suite level. So, in such a case, all the classes inside that test level will share the overridden parameter, and other classes that are outside the test level will share the suite level parameter.

thread2.PNG

Troubleshooting

Issue 1: It will throw an error if the parameter value in testng.xml cannot be typecasted to the corresponding test methods parameter.

Consider the example below

author.PNG

When you run the TestNG.xml file it will throw a below exception

errorauthor.PNG

Here, the ‘author’ attribute is equal to ‘Test’ which is a string and in the corresponding test method it is expecting an integer value, so we will get an exception.

Issue 2: Your @Parameters do not have a corresponding value in testing.xml.

You can overcome this situation by adding @optional annotation in the corresponding parameter in the test method.

issue2.PNG

Issue 3: Using Testng.xml if you want to test multiple values for the same parameter.

You can have multiple and different parameters, but each parameter can only have a single value. This can help by hardcoding values into the script which makes the code reusable. If you want to use multiple values for the same parameter we will use DataProviders.

Parameterization in TestNG scripts for automated browser testing

Let us consider the below scenario by printing a specific browser value by passing the browser name as a parameter.

Java Code:

Step 1: Create a Package (parameterization)Step 2: Create a Class (PassingParameter)

package parameterization;

import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

public class PassingParameter {
@Parameters("browser")
  @Test
  public void test(String browser) {
      if(browser.equalsIgnoreCase("FF"))
          {
            System.out.println("The browser value is : " +browser);
          }else if(browser.equalsIgnoreCase("Chrome"))
          {
                  System.out.println("The browser value is : " +browser);
                 
          }else if(browser.equalsIgnoreCase("IE"))
          {
                  System.out.println("The browser value is : " +browser);
          }
          else
          {
                  System.out.println("Incorrect browser value passed.");
          }  
  }
}

Step 3: Create a TestNG.xml file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
  <test name="Firefox Test">
   <parameter name="browser" value="FF"/>
    <classes>
   <class name="parameterization.PassingParameter"/>
    </classes>
  </test>
 
  <test name="IE Test">
   <parameter name="browser" value="IE"/>
    <classes>
   <class name="parameterization.PassingParameter"/>
    </classes>
  </test>
  <test name="Incorrect Browser">
  <parameter name="browser" value="ABC"/>
    <classes>
   <class name="parameterization.PassingParameter"/>
    </classes>
  </test>
 </suite>

Run the above Testng.xml file and you will see the below output in console:

Output1.PNG

In the test results summary you’ll see that the parameter values passed in the XML is also printed when you go by the detailed results:

output2.PNG

The emailable report shows that the different parameters are passed to different tests to consolidate the results easy:

output3.PNG
output4.PNG

Now let us try to understand how we can leverage combinations of parameters for Selenium automation testing by passing 2 parameters to our test. Make note that whatever number of parameters you pass to your test you need to accept the same number of parameters in your test method in a correct sequence.

Let us consider the below scenario by printing a specific browser and URL value by passing the browser name as well as URL as a parameter.

Step 1: Create a Package (parameterization

Step 2: Create a Class (PassingMultipleParameters)

package parameterization;

import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

public class PassingMultipleParameters {
@Parameters({"url","browser"})
@Test
public void test1(String url,String browser)
{
        if(browser.equalsIgnoreCase("FF"))
        {
                System.out.println("The browser value is : " +browser);
        }
        else if(browser.equalsIgnoreCase("Chrome"))
        {
                System.out.println("The browser value is : " +browser);
        }
        else if(browser.equalsIgnoreCase("IE"))
        {
                System.out.println("The browser value is : " +browser);
        }
        else
        {
                System.out.println("Incorrect browser value passed.");
        }
}
}

Step 3: Create a TestNG.xml file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
  <test name="Firefox Test">
   <parameter name="url" value="http://www.yahoo.com"/>
   <parameter name="browser" value="FF"/>
    <classes>
   <class name="parameterization.PassingMultipleParameters"/>
    </classes>
  </test>
 
  <test name="Chrome Test">
   <parameter name="url" value="http://www.google.com"/>
   <parameter name="browser" value="Chrome"/>
    <classes>
   <class name="parameterization.PassingMultipleParameters"/>
    </classes>
  </test>
</suite>

Run the above Testng.xml file and you will see the below output in console:

output5.PNG

In the test results summary you’ll see that the parameter values passed in the XML are also printed when you go by the detailed results:

output6.PNG

Email Report:

emailoutput.PNG
emailoutput1.PNG

Parameterization using Data Providers in TestNG

In the above session, we have seen how to use the @Parameters annotation in our framework to get various results. Now, going further we will come across cases where we will have to use test scripts against multiple sets of data. Sometimes we might need huge sets of data to be used in a single execution. This type of testing is also called Data-Driven Testing. This can be achieved by using @DataProvider annotation in our TestNG framework.

 The Syntax for @DataProvider annotation.

@DataProvider(name=”SearchProvider”)

public Object[ ][ ] TestDataFeed(){

Before we start using it for parameterization in TestNG first we need to note two points. @DataProvider has only one attribute i.e ‘name’ and secondly, it is optional and in that case, you do not specify it, then the name would be the same as the corresponding method name. This Data Provider returns a 2-Dimensional array object.

Now let us try to understand by using a simple example where we will try to pass the monument and city name using data provider. You can further use this example as a base to login to Gmail or Facebook using different email ID and passwords.

Let us consider the scenario to Open google.com and search 3 keywords with separate combinations.

Let us follow the below steps to get this executed:

  1. Launch the browser to open www.google.com
  2. Search the first keyword combination.
  3. Get the page title.
  4. Repeat steps 2 & 3 for the other 2 keywords combination.
  1.  
    MONUMENTCITY
GateWay Of IndiaMumbai
Konark Sun TempleKonark
Statue Of UnityGujarat

Java Code:

Step 1: Create a Package (dataprovider)Step 2: Create a Class Data_Provider

package dataProvider;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class Data_Provider {
  WebDriver driver;
  
  @Test(dataProvider="searchText") 
  public void paramByDataProvider(String monument,String city) throws InterruptedException {
  //Initializing Driver
System.setProperty("webdriver.gecko.driver","F:\\drivers\\geckodriver.exe");  
        driver = new FirefoxDriver();
        //Opening search engine
        driver.get("https://google.com");
  WebElement searchBox=driver.findElement(By.name("q"));
  searchBox.sendKeys(monument +" "+city);
  System.out.println("You are trying to search " +monument+ " which is in " +city);
   
  WebElement srchBtn = driver.findElement(By.name("btnK"));
  srchBtn.submit();
  Thread.sleep(3000);
  System.out.println("The page title is: " +driver.getTitle());
    driver.quit();
  }
 
 
  /*Data Provider which returns Object[][] wherein
  * The first column has 'monument' and the second one has 'city'
  **/
  @DataProvider(name="searchText")
  public Object[][] getDataProviderData(){
        Object[][] searchWords=new Object[3][2];
        //Enter data into Object Array
        searchWords[0][0]="Gate Way Of India";
        searchWords[0][1]="Mumbai";
        searchWords[1][0]="Konark Sun Temple";
        searchWords[1][1]="Konark";
        searchWords[2][0]="Statue of Unity";
        searchWords[2][1]="Gujarat";
    return searchWords;
   
  }

}

Step 3: Create a TestNG.xml file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
  <test name="Firefox Test">
    <classes>
   <class name="dataprovider.Data_Provider"/>
</classes>
  </test>
 </suite> <!-- Suite -->

We will get the output like below which summarizes the data providers passed and the corresponding results:

dataprov1.PNG

Emailable Report:

dataprov2.PNG
dataprov3.PNG

In the above example, we tried with data providers being invoked from the same class. You can also invoke data providers from another class by simply making the data provider method static and provide the data provider class in the test method annotation. 

Let us consider the example by creating a different class for our data provider.

Java Code for Data Provider Class:Step 1: Create a Class

package dataprovider;

import org.testng.annotations.DataProvider;

public class DataProviderClass {
@DataProvider(name="searchText")
  public static Object[][] getDataProviderData(){
             Object[][] searchWords=new Object[3][2];
             //Enter data into Object Array
             searchWords[0][0]="Gate Way Of India";
        searchWords[0][1]="Mumbai";
        searchWords[1][0]="Konark Sun Temple";
        searchWords[1][1]="Konark";
        searchWords[2][0]="Statue of Unity";
        searchWords[2][1]="Gujarat";
  
                return searchWords;
           }

}

Java Code for the Class from where Data Provider is called:
package dataprovider;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.Test;

public class ClassLevelDataProvider {
WebDriver driver;
@Test(dataProvider="searchText",dataProviderClass=DataProviderClass.classpublic void paramByDataProvider(String monument,String city) throws InterruptedException {
  //Initializing Driver
System.setProperty("webdriver.gecko.driver","F:\\drivers\\geckodriver.exe");  
        driver = new FirefoxDriver();
        //Opening search engine
        driver.get("https://google.com");
  WebElement searchBox=driver.findElement(By.name("q"));
  searchBox.sendKeys(monument +" "+city);
  System.out.println("You are trying to search " +monument+ " which is in " +city);
   
  WebElement srchBtn = driver.findElement(By.name("btnK"));
  srchBtn.submit();
  Thread.sleep(3000);
  System.out.println("The page title is: " +driver.getTitle());
    driver.quit();
}
}

Step 2: Create a TestNG.xml file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
  <test name="Firefox Test">
    <classes>
   <class name="dataprovider.ClassLevelDataProvider"/>
</classes>
  </test>
  </suite> <!-- Suite -->

Run the above TestNG.xml file and you can see that we receive the same results as we got before.

Data Provider annotation with parameters using Method and ITestContext.

Example 1: Using Method Parameter in TestNG

We use method parameters in TestNG if we want the same data provider to work differently for different methods like in a case where we test different data sets for different test methods. 

Let us consider the below example:

  • Check if the method name is domesticMonuments
  • If it returns one set of value
  • If not then return another set of value

Java Code:Step 1: Create a Class (dataProviders)

package dataprovider;

import java.lang.reflect.Method;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class dataProviders {
WebDriver driver;
    @BeforeMethod
   public void setUp(){
         //Initializing Driver
System.setProperty("webdriver.gecko.driver","F:\\drivers\\geckodriver.exe");  
                     driver = new FirefoxDriver();
                     //Opening search engine
                     driver.get("https://google.com");
               }
   @AfterMethod
   public void tearDown(){
         driver.quit();
   }
   @Test(dataProvider="destinations")
   public void domesticMonuments(String monument,String city) throws InterruptedException{
         WebElement searchBox=driver.findElement(By.name("q"));
         searchBox.sendKeys(monument +" "+city);
         System.out.println("You are trying to search " +monument+ " which is in " +city); 
         WebElement srchBtn = driver.findElement(By.name("btnK"));
         srchBtn.submit();
         Thread.sleep(3000);
         System.out.println("The page title is: " +driver.getTitle());
           }
   @Test(dataProvider="destinations")
   public void intlDestinations(String location) throws InterruptedException{
         WebElement searchBox=driver.findElement(By.name("q"));
         searchBox.sendKeys(location);
         System.out.println("You are trying to search : " +location);
         WebElement srchBtn = driver.findElement(By.name("btnK"));
         srchBtn.submit();
         Thread.sleep(3000);
         System.out.println("The page title is: " +driver.getTitle());  
   }
   @DataProvider(name="destinations")
     public Object[][] getDataProviderData(Method m){
         if(m.getName().equalsIgnoreCase("domesticMonuments")){
               return new Object[][]{
                     { "Gate Way of India", "Mumbai" },
                     { "Konark Sun Temple", "Konark" },
                     { "Statue of Unity", "Gujarat" }
               };
               }
         else{
               return new Object[][]{
                     {"London"},
                     {"Australia"},
                     {"Dallas"}
               };
         }  
     }

}

Step 2: Create a TestNG.xml file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" >
  <test name="Firefox Test">
    <classes>
   <class name="dataprovider.dataProviders"/>
</classes>
  </test>
  </suite>

When you run the above TestNG.xml file you will be able to see the details of the data providers used.

Method.PNG
method1.PNG
destinations.PNG

Example 2: Using ITestContext Parameter in TestNG

Suppose my test methods are assigned to different groups and I need to use different test data for different groups. In such scenario we can use ITestContext parameter with our Data Provider annotation. 

Let us consider the below example.

Java Code:

package dataprovider;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.ITestContext;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class ITestContext_DataProvider {
WebDriver driver;
   @BeforeMethod(groups={"One","Two"})
   public void setUp(){
         //Initializing Driver
System.setProperty("webdriver.gecko.driver","F:\\drivers\\geckodriver.exe");  
                     driver = new FirefoxDriver();
                     //Opening search engine
                     driver.get("https://google.com");
               }
   @AfterMethod(groups={"One","Two"})
   public void tearDown(){
         driver.quit();
   }
  
   @Test(dataProvider="searchKey" , groups="One")
   public void testMethodOne(String monument,String city) throws InterruptedException{
         WebElement searchBox=driver.findElement(By.name("q"));
         searchBox.sendKeys(monument +" "+city);
         System.out.println("You are trying to search " +monument+ " which is in " +city);
          
         WebElement srchBtn = driver.findElement(By.name("btnK"));
         srchBtn.submit();
         Thread.sleep(3000);
         System.out.println("The page title is: " +driver.getTitle());
   }
   @Test(dataProvider="searchKey" , groups="Two")
   public void testMethodTwo(String location) throws InterruptedException{
 
         WebElement searchBox=driver.findElement(By.name("q"));
         searchBox.sendKeys(location);
         System.out.println("You are trying to search : " +location);
         WebElement srchBtn = driver.findElement(By.name("btnK"));
         srchBtn.submit();
         Thread.sleep(3000);
         System.out.println("The page title is: " +driver.getTitle());  
   }
   @DataProvider(name="searchKey")
     public Object[][] getDataProviderData(ITestContext c){
         Object[][] grpArr = null;
         for (String grp : c.getIncludedGroups()){
               if(grp.equalsIgnoreCase("One")){
                     grpArr = new Object[][]{
                     { "Gate Way of India", "Mumbai" },
                         { "Konark Sun Temple", "Konark" },
                         { "Statue of Unity", "Gujarat" }
                 
                     };
                     break;
               }
               else if(grp.equalsIgnoreCase("Two")){
                     grpArr = new Object[][]{
                     {"London"},
                         {"Australia"},
                         {"Dallas"}
                 };
               }
         }
         return grpArr; 
     }
}

Create a TestNG.xml file

<?xml version="1.0" encoding="UTF-8"?>
<suite name="Suite" >
  <test name="First DP Run">
    <groups>
       <run>
         <include name = "One" />
      </run>
   </groups>
    <classes>
   <class name="dataprovider.ITestContext_DataProvider"/>
</classes>
  </test>
  <test name="Second DP Run">
    <groups>
       <run>
         <include name = "Two" />
      </run>
   </groups>
    <classes>
   <class name="dataprovider.ITestContext_DataProvider"/>
</classes>
  </test> 
 </suite>

After running the above xml file you will get the results like below:

dallas.PNG

Conclusion:

  • We require Parameterization to create Data Driven Testing.
  • TestNG support two types of parameterization, using @Parameter+TestNG.xml and using@DataProvider
  • In @Parameter+TestNG.xml parameters can be placed in suite level and test level. If the same parameter name is declared in both places; first we will get preference to the test level parameter over suit level parameter.
  • Only one value can be set at a time by using @Parameter+TestNG.xml, but @DataProvider return the 2nd array of Object.
  • If DataProvider is present in a different class then the class where the test method resides, DataProvider should be a static method.

There are two types of parameters supported by DataProvider i.e. Method and ITestContext.

Facebook Comments
Tags

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Back to top button
Close
Close