DataProvider In TestNG

DataProvider In TestNG: TestNg is a vibrant featured automation framework with lots of functionality. We can parametrize the configuration and test methods so that we can execute the same test method with a different set of input data. To Make it happen, we must make that test method a parametrized one, and we must pass the input value from somewhere.

We can do this in 2 ways:

  • Parameters from testng.xml
  • Parameters from DataProvider

In our previous article, we have already discussed a different way and how to implement parameters in the testng class. If you have not checked, you can follow this link.

Now, one question comes to mind: When we can use the parameters, what’s the requirement of DataProvider?

  • What are the factors to use DataProvider over testng.xml?
  • When should we go for testng.xml, DataProvider, or both?

Both the ways of providing parameter values in TestNG have their advantages. We will compare them here:

  • To pass input data to a method, we can do that by mentioning the data in testng.xml and passing it through using the parameter. But if you need to change the input data in the future, then an automation tester can understand that easily. Still, if any manual testers do this job, it’s challenging to understand the XML file. But if you pass data from an Excel or properties file or Database, it will be easier to update data for anyone as per their required data to run tests. However, the problem is that we can not read the data from an external source using the parameter tag.
  • If your automation script requires multiple sets of data, then it’s not possible through testng.xml
  • If the input data values are decided during the run time, it is impossible in testng because we can not modify the testng.xml file at the runtime.
  • You can pass any values, including the user-defined class type, which is also impossible through testng.

What Is DataProvider?

  • DataProvider is an annotation that is used to mark a method as a DataProvider, which provides data in the form of an array of objects, and the data also can be used by configuration (@after & @before methods) and test method of a testng class.
  • You can declare any method as a data provider by adding a method signature by following this simple syntax:
    @DataProvider(name=”Any name”)
  • A DataProvider method can have a name so that it can be used to supply data to test methods by just mentioning its name.
  • A DataProvider annotated method always returns an array of objects. It can be any dimensional.
  • The number of values passed by a data provider method should equal the number of attributes of the test method on which the data provider method is used.
  • If the attributes do not match, you will get an exception “org.testng.internal.reflect.MethodMatcherException: Data provider mismatch”.
  • The main difference between passing data from the DataProvider method and passing data from the testng.xml file is that by using DataProvider, we can pass multiple sets of data using DataProvider, and the test method will run on each set of data. However, using parameters and testng.xml, it is impossible to pass multiple sets of data.
  • If you are using DataProvider, you are not required to test XML to run it as we need to in parameter annotation.

Scenario 1: A data provider method which provides N set of single data:

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderBasics1 
{

   // You must need to mention data provider method name in Test method
   @Test(dataProvider="DataContainer")
   public void methodWithSingleAttribute(String a) 
   {
      System.out.println(a);
   }

   // A data provider which will provide single value to a test method thrice.
   @DataProvider(name="DataContainer")
   public Object[] myDataProvider() 
   {
      // Passing 3 set of data and each set contains single value
      Object data[]= new Object[3];
      data[0]= "Make";
      data[1]= "Selenium";
      data[2]= "Easy";
      return data;
   }
}

Explanation:

  • From the output, we have seen that the test method is run three times, and each time, it is counted as a separate test. That’s why the total test run count is three.

Why are we using The two-dimensional array data provider method?

Most of the time, and also in some interviews, you face this question, why are we using a two-dimensional array with dataProvider? Before going to that, let us try to understand with a scenario:

Suppose a new office has opened in your locality. Suppose five employees joined their organization. For that, they need to register new employees in their database, and for that, they have created an application. For complete registration purposes, they require that person’s first name, last name, and phone number.

So the total requirement here is:

  • Employees First, Last Name & Phone Number
  • Five employees need to be registered

Table:

First_Name Last_nameEmail_ID
Mukesh Otwani Motwani@gmail.com
Amod Mahajan amahajan@hotmail.com
Animesh Prashant aprashant@gmail.com
Ankur Singhasingh@gmail.com
Amritansh Kumar akumar@gmail.com

If you saw the above, you can find out each row represents an employee’s details, and each column represents required input data like First, last name, and phone number.

The same logic is applied in DataProvider, As we store the data in an Excel file where we can find each cell with the help of the cell details [row][column]. That’s why we are creating a two-dimensional array.

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderBasics2 
{

   // You must need to mention data provider method name in Test method
   @Test(dataProvider="DataContainer")
   public void studentRegistration(String First_name, String Last_Name, String Email_ID) 
   {
      System.out.println("Registered student with details: "+First_name+" "+Last_Name+" "+Email_ID);
   }

   // A data provider method with return type as 2D array
   @DataProvider(name="DataContainer")
   public Object[] myDataProvider() {

      Object data[][]= new Object[5][3];
      // First student details
      data[0][0]= "Mukesh";
      data[0][1]= "Otwani";
      data[0][2]= "Motwani@gmail.com";

      // Second student details
      data[1][0]= "Amod";
      data[1][1]= "Mahajan";
      data[1][2]= "amahajan@hotmail.com";

      // Third student details
      data[2][0]= "Animesh";
      data[2][1]= "Prashant";
      data[2][2]= "aprashant@gmail.com";

      // Fourth student details
      data[3][0]= "Ankur";
      data[3][1]= "Singh";
      data[3][2]= "asingh@gmail.com";

      // Fifth student details
      data[4][0]= "Amritansh";
      data[4][1]= "Kumar";
      data[4][2]= "akumar@gmail.com";

      return data;

   }
}

As in the above example, we have assigned the values by using the index, but we can also do the same thing by creating inline arrays like below:

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderBasics3 
{
   // You must need to mention data provider method name in Test method
   @Test(dataProvider="DataContainer")
   public void studentRegistration(String First_name, String Last_Name, String Email_ID) 
   {
      System.out.println("Registered student with details: "+First_name+" "+Last_Name+" "+Email_ID);

   }

   // A data provider method with return type as 2D array
   @DataProvider(name="DataContainer")
   public Object[] myDataProvider() {
      return new Object[][]
            {
         {"Mukesh", "Otwani", "Motwani@gmail.com"},
         {"Amod","Mahajan","Amahajan@hotmail.com"},
         {"Animesh","Prashant","aprashant@gmail.com"},
         {"Ankur","Singh","asingh@gmail.com"},
         {"Amritansh", "Kumar","akumar@gmail.com"}

            };

   }
}

How do you access data provider methods from another class test class?

In the previous example, we used the DataProvider and test methods in a single class. However, it is recommended to store data and test separately. So we can do that in 2 ways:

  • Using Inheritance
  • Static or Non-static DataProvider Methods

DataProvider Using Inheritance

In this technique, we can put the dataProvider method in a class and the test method in another class, but the test method class will extend the dataProvider class.

So when you execute, TestNg first looks at the same class for the dataProvider method, and if it is not found in the dataProvider in testng class, it will look in the dataProvider class.

DataProvider Class:

package DataProvider;
import org.testng.annotations.DataProvider;
public class DataProviderClass 
{
   // A data provider method with return type as 2D array
   @DataProvider(name = "DataContainer")
   public Object[] myDataProvider() 
   {
      return new Object[][] 
            {
         {"Mukesh", "Otwani", "Motwani@gmail.com"},
         {"Amod","Mahajan","Amahajan@hotmail.com"},
         {"Animesh","Prashant","aprashant@gmail.com"},
         {"Ankur","Singh","asingh@gmail.com"},
         {"Amritansh", "Kumar","akumar@gmail.com"}

            };
   }
}

TestNG Class:

package DataProvider;
import org.testng.annotations.Test;
// Extend DataProvider class
public class DataProviderBasics3 extends DataProviderClass
{
   // You must need to mention data provider method name in Test method
   @Test(dataProvider = "DataContainer")
   public void studentRegistration(String First_name, String Last_Name, String Email_ID) 
   {
      System.out.println("Registered student with details: " + First_name + " " + Last_Name + " " + Email_ID);

   }

}

By Making Static or Non-static DataProvider Methods

If there is a requirement for extending a different class, then at a single place, you can not extend multiple classes as multiple inheritances are not allowed in Java. In that case, you can create static or non-static DataProvider methods and can give it a reference in the TestNG class using the “dataProviderClass” attribute in the @Test method.

So when will you create static, and when do you need to create non-static methods?

If your DataProvider class has parameterized constructors, declare dataprovider methods as STATIC. If your DataProvider has a default or no-argument constructor, you can declare data to provide static or non-static methods.

The below class has no constructor. So we can mark the data provider method as static or non-static.

package DataProvider;
import org.testng.annotations.DataProvider;
public class DataProviderClass 
{
   // A data provider method with return type as 2D array
   @DataProvider(name = "DataContainer")
   public static Object[] myDataProvider() 
   {
      return new Object[][] 
            {
         { "Mukesh", "Otwani", "Motwani@gmail.com" },
         { "Amod", "Mahajan", "Amahajan@hotmail.com" },
         { "Animesh", "Prashant", "aprashant@gmail.com" },
         { "Ankur", "Singh", "asingh@gmail.com" },
         { "Amritansh", "Kumar", "akumar@gmail.com" }

            };

   }
}

OR

package DataProvider;
import org.testng.annotations.DataProvider;
public class DataProviderClass 
{
   // A data provider method with return type as 2D array
   @DataProvider(name = "DataContainer")
   public Object[][] myDataProvider() 
   {
      return new Object[][] 
            {
         { "Mukesh", "Otwani", "Motwani@gmail.com" },
         { "Amod", "Mahajan", "Amahajan@hotmail.com" },
         { "Animesh", "Prashant", "aprashant@gmail.com" },
         { "Ankur", "Singh", "asingh@gmail.com" },
         { "Amritansh", "Kumar", "akumar@gmail.com" }

            };

   }
}

TestNG Class

package DataProvider;
import org.testng.annotations.Test;
// Extend DataProvider class
public class DataProviderBasics3
{
   // You must need to mention data provider method name in Test method
   @Test(dataProvider = "DataContainer", dataProviderClass = DataProviderClass.class)
   public void studentRegistration(String First_name, String Last_Name, String Email_ID) 
   {
      System.out.println("Registered student with details: " + First_name + " " + Last_Name + " " + Email_ID);

   }

}

If your data provider class has a parameterised constructor, you should declare the Data Provider method static.

package DataProvider;
import org.testng.annotations.DataProvider;
public class DataProviderClass 
{
   DataProviderClass(String s)
   {
      System.out.println("I am parameterised constructor so you can not have non-sttaic data"
      + "provider methods");
   }

   // A data provider method with return type as 2D array
   @DataProvider(name = "DataContainer")
   public static Object[][] myDataProvider() 
   {
      return new Object[][] 
            {
         { "Mukesh", "Otwani", "Motwani@gmail.com" },
         { "Amod", "Mahajan", "Amahajan@hotmail.com" },
         { "Animesh", "Prashant", "aprashant@gmail.com" },
         { "Ankur", "Singh", "asingh@gmail.com" },
         { "Amritansh", "Kumar", "akumar@gmail.com" }

            };

   }
}

TestNG Class:

package DataProvider;
import org.testng.annotations.Test;
// Extend DataProvider class
public class DataProviderBasics3
{
   // You must need to mention data provider method name in Test method
   @Test(dataProvider = "DataContainer", dataProviderClass = DataProviderClass.class)
   public void studentRegistration(String First_name, String Last_Name, String Email_ID) 
   {
      System.out.println("Registered student with details: " + First_name + " " + Last_Name + " " + Email_ID);

   }

}

Is It Mandatory To Have Return Type as Object in the DataProvider Method?

In our previous example, we have seen the dataProvider looking like below:

// A data provider method with return type as 2D array
@DataProvider(name = "DataContainer")
public Object[] myDataProvider()
{
   return new Object[][] {
      { "Mukesh", "Otwani", "Motwani@gmail.com" },
      { "Amod", "Mahajan", "Amahajan@hotmail.com" },
      { "Animesh", "Prashant", "aprashant@gmail.com" },
      { "Ankur", "Singh", "asingh@gmail.com" },
      { "Amritansh", "Kumar", "akumar@gmail.com" }

   };

}

As you can say the return type of the method is a two-dimensional array, So one doubt comes into our mind is it compulsory that each method should have a return type as an object only?

The answer to that, it is not mandatory that always the dataProvider should return a two-dimensional array. If all the data are string, then you can also return string.

Sample Java Code:

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderWithReturnType 
{

   // You must need to mention data provider method name in Test method
   @Test(dataProvider = "DataContainer")
   public void studentRegistration(String First_name, String Last_Name, String Email_ID) 
   {
      System.out.println("Registered student with details: " + First_name + " " + Last_Name + " " + Email_ID);

   }

   // A data provider method with return type as 2D array
   @DataProvider(name = "DataContainer")
   public static String[][] myDataProvider() 
   {
      return new String[][] {
         {"Mukesh", "Otwani", "Motwani@gmail.com"},
         {"Amod", "Mahajan", "Amahajan@hotmail.com"},
         {"Animesh", "Prashant", "aprashant@gmail.com"},
         {"Ankur", "Singh","asingh@gmail.com"},
         {"Amritansh", "Kumar", "akumar@gmail.com"}

      };

   }
}

Can The DataProvider Return a Primitive data type?

Let’s first see a sample program:

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderWithReturnType 
{
   // You must need to mention data provider method name in Test method
   @Test(dataProvider = "DataContainer")
   public void studentRegistration(int n1, int n2, int n3) 
   {
      System.out.println("n1 =" + n1 + " n2 =" + n2 + " n3 =" + n3);
   }

   // A data provider method with return type as 2D array
   @DataProvider(name = "DataContainer")
   public static int[][] myDataProvider() 
   {
      return new int[][] 
            {
         {1, 2, 3},
         { 3, 4, 5}
            };
   }
}

If we run the above program where we are returning primitive data type, then we are getting the ClassCastException exception. Let us update the same program with the return type as a wrapper class.

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderWithReturnType 
{

   // You must need to mention data provider method name in Test method
   @Test(dataProvider = "DataContainer")
   public void studentRegistration(int n1, int n2, int n3) 
   {
      System.out.println("n1 ="+n1 + " n2 ="+n2 +" n3 ="+n3);

   }

   // A data provider method with return type as 2D array
   @DataProvider(name = "DataContainer")
   public static Integer[][] myDataProvider() 
   {
      return new Integer[][] { { 1,2,3 },
         { 3,4,5}

      };

   }
}

The program was executed successfully without any errors.

Note: You Can always use the return type as a wrapper class, and if you have used the return type as primitive, you will get “ClassCastException”.

From all the above examples, we got to know that a dataProvider method doesn’t need always to return only a two-dimensional object array; you can use other data types also. But the return type is an object, which is preferred when we need to return heterogeneous data — e.g., Integer, String, etc.- all from one method.

Encapsulation with DataProvider Method?

Encapsulation is one of the critical OOPS concepts of Java Programming language, and It is nothing but binding related stuff together under one roof. It makes it simpler for users to do operations like add, remove, and modify members.

As in our previous example, we have mentioned the registration process of an employee we need a few details like the first name. Last name and employee ID and we can do the registration with the help of the registration method. and we are doing all these operations by putting data member and member functions on a single class.

By using that class and method, we can register as many employees as per our requirement, like below:

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderBasics3 
{

   // You must need to mention data provider method name in Test method
   @Test(dataProvider="DataContainer")
   public void studentRegistration(String First_name, String Last_Name, String Email_ID) 
   {
      System.out.println("Registered student with details: "+First_name+" "+Last_Name+" "+Email_ID);

   }

   // A data provider method with return type as 2D array
   @DataProvider(name="DataContainer")
   public Object[] myDataProvider() 
   {
      return new Object[][]
            {
         {"Mukesh", "Otwani", "Motwani@gmail.com"},
         {"Amod","Mahajan","Amahajan@hotmail.com"},
         {"Animesh","Prashant","aprashant@gmail.com"},
         {"Ankur","Singh","asingh@gmail.com"},
         {"Amritansh", "Kumar","akumar@gmail.com"}

            };

   }
}

But Later, you get another new requirement: you need to mention the Gender of that employee at the time of registration. So to achieve this, we need to modify the argument of the dataprovider and test method.

It is a single argument, so we can do that, but if there is a requirement to add and remove the number of arguments, then its too difficult to handle such a situation.

So, in this case, we can use the help of encapsulation to handle such a modification in the programs.

So follow the steps:

  • Create an employee class, where define the properties, methods for performing some operations, and data getter and setter.
  • Create another class having test and dataProvider methods

Employee Class:

package DataProvider;
public class Student 
{

   String name;
   int age;
   String gender;

   public Student(String name, int age, String gender)
   {
      this.name= name;
      this.age=age;
      this.gender=gender;
   }

   // A method to check if student is eligible for registration
   public boolean validateAge()
   {
      if(this.age>2)
      {
         System.out.println("You are eligible to do registration.");
         return true;
      }
      else
         return false;
   }

}

DataProvider class:

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class StudentDataProvider 
{

   // A data provider method with return type as 2D array
   @DataProvider(name = "DataContainer")
   public static Student[] myDataProvider() 
   {

      return new Student[] { new Student("Amod", 4, "Male"), new Student("Neha", 1, "Female")

      };

   }

   // You must need to mention data provider method name in Test method
   @Test(dataProvider = "DataContainer")
   public void methodWithSingleAttribute(Student a) 
   {

      if(a.validateAge())
      {
         System.out.println("Student registered with details as Name = "+a.name + " , Age ="+a.age +" , Gender ="+a.gender);
      }
      else
      {
         System.out.println("Student not registered with details as Name = "+a.name + " , Age ="+a.age +" , Gender ="+a.gender);
      }

   }
}

In the above dataProvider class, you can say that we are not returning any number of parameters, here, we are returning an object. That solves our add or remove the argument problems. In this case, we do not need to modify the number of arguments in the caller test method.

Lazy Initialisation of DataProvider Method

We have just seen if the number of arguments is more in dataProvider if you don’t know the required number of data in advance or if the number of parameters is dynamic, then its too difficult to handle such situations using dataProvider. Because in all these cases, we can not define the DataProvider and Test methods easily. All these problems can be solved using an iterator for a list of data.

As per the testNG Official documents:

An Iterator<Object[]>. The only difference with Object[][] is that an Iterator lets you create your test data lazily. TestNG will invoke the iterator and then the test method with the parameters returned by this iterator one by one. This is particularly useful if you have a lot of parameter sets to pass to the method and don’t want to create all of them upfront.
An array of objects (Object[]). This is similar to Iterator<Object[]> but causes the test method to be invoked once for each element of the source array.
An Iterator<Object>>. The lazy alternative of Object[]. This causes the test method to be invoked once for each element of the iterator.

That means we get a set of data for a test method in each iteration.

Student Class:

package DataProvider;
public class Student 
{

   String name;
   int age;
   String gender;

   public Student(String name, int age, String gender)
   {
      this.name= name;
      this.age=age;
      this.gender=gender;
   }

   // A method to check if student is eligible for registration
   public boolean validateAge()
   {
      if(this.age>2)
      {
         System.out.println("You are eligible to do registration.");
         return true;
      }
      else
         return false;
   }

}

DataProvider Class:

package DataProvider;
import java.util.ArrayList;
import java.util.Iterator;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderAndIterator
{

   // A DataProvider method which returns an iterator to a list.
   @DataProvider(name="DataProviderIterator")
   public Iterator<Student> studentDetails(){
      ArrayList<Student> s=new ArrayList<Student>();
      s.add(new Student("Kiran", 28, "Male"));
      s.add(new Student("Amod", 28, "Male"));
      s.add(new Student("Anu", 28, "Female"));
      return s.iterator();
   }

   // Test method accepting Student type attribute ( Not an Iterator)
   @Test(dataProvider = "DataProviderIterator")
   public void Studentregistration(Student student) 
   {

      if(student.validateAge())
      {
         System.out.println("Student registered with details as Name = "+student.name + " , Age ="+student.age +" , Gender ="+student.gender);
      }
      else
      {
         System.out.println("Student not registered with details as Name = "+student.name + " , Age ="+student.age +" , Gender ="+student.gender);
      }

   }

}

You can see that the Iterator can remove the dependency of defining arguments in advance. You can easily add/remove/modify members and logic without worrying about the mismatch of arguments between the DataProvider method and the Test method.

Parametrising DataProvider Method to Provide Data to Multiple Test Methods:

Previously, we have seen the use of DataProvider With a single @test method, But now we will see how we can use a single DataProvider and multiple @test methods.

For the use of dataProvider, we can use separate dataProvider for each test method, which is a simple way but managing multiple dataProvider when the count of test methods more is a difficult task. So to deal with such cases, we can parametrize the DataProvider method to return the required data set as per the test method name.

Let us see how we can do this:

As per the Official Statement On the TestNG Official website:

If you declare your @DataProvider as taking a java.lang.reflect.The method as the first parameter TestNG will pass the current test method for this first parameter. This is particularly useful when several test methods use the same @DataProvider, and you want it to return different values depending on which test method it supplies data for.

That means when we pass a method name as the first parameter to the dataProvider method, TestNG will pass the current test method name for this first parameter, and based on that data, we can decide what data needs to be returned.

And there, we can use the conditional block statements to accommodate the number of test methods as we know that the return type of a dataProvider is an Object[], which helps us to return a different set of data as well.

package DataProvider;
import java.lang.reflect.Method;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

// Data class
class India 
{
   public String getCapital() 
   {
      return "New Delhi";
   }
}

class USA 
{
   public String getCapital() 
   {
      return "New York";
   }
}

class UK 
{
   public String getCapital() 
   {
      return "London";
   }
}

public class CustomDataProvider 
{

   // A data provider method which will return a required data based on Test method
   // name
   @DataProvider(name = "DataContainer")
   public static Object[] myDataProvider(Method m)
   {

      // Matching test method name
      // You can use switch as well.
      if (m.getName().equals("India")) 
      {
         India i[] = new India[1];
         i[0] = new India();
         return i;
      }
      else if (m.getName().equals("USA")) 
      {
         USA i[] = new USA[1];
         i[0] = new USA();
         return i;
      }
      else if (m.getName().equals("UK")) 
      {
         UK i[] = new UK[1];
         i[0] = new UK();
         return i;
      }
      else
      {
         System.out.println("No data found");
         //System.exit(0);
         return null;
      }

   }

   // A test method
   @Test(dataProvider = "DataContainer")
   public void India(India capitalName) 
   {
      System.out.println("Capital of India is " + capitalName.getCapital());
   }

   // A test method
   @Test(dataProvider = "DataContainer")
   public void USA(USA capitalName) 
   {
      System.out.println("Capital of USA is " + capitalName.getCapital());
   }

   // A test method
   @Test(dataProvider = "DataContainer")
   public void UK(UK capitalName) {
      System.out.println("Capital of UK is " + capitalName.getCapital());
   }
}

Passing External Data File Name to DataProvider

In the above discussion, we learn how to pass test data from one DataProvider method to multiple methods, which helps us get more benefits of TestNG. But now we will learn one more advanced concept of TestNg, which is how we can pass the external data file name to the dataProvider method using the TestNG XML.

As we know to store test data permanently, we are using external files like Excel and Properties files. Where the external file name is not constant, for example, an Excel sheet can have multiple datasheets, and we pass the different datasheet names as per the requirement so we can not hardcoded the external file name in the script. Still, we can follow the below technique, where we can use the concept of the parameter from testng.xml and DataProvider.

In TestNg, We have an interface called ITestContext, which is mainly used to store and share the data across the tests in the testNg framework. So we can use the ITestContext in the dataProvider method as an attribute which can be used to read the data from the testng.xml, and in this way, we can pass the required file name data from testNG.xml to the dataProvider method.

If the Data Filename changes, we can do the same in the testng.xml file, and there is no need to make any changes in dataProvider methods.

Sample Class:

package DataProvider;
import java.lang.reflect.Method;
import org.testng.ITestContext;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class CustomDataProviderWithParameter 
{

   // A data provider method which will return a required data based on Test method
   // name
   @DataProvider(name = "DataContainer")
   public static Object[] myDataProvider(Method m, ITestContext iTestContext) 
   {

      // Getting the method name
      String methodName = m.getName();

      switch(methodName)
      {
      case "A" :
      {
         // Getting parameter value from testng.xml to get data file name for desired method
         String dataFilename= iTestContext.getCurrentXmlTest().getParameter("DataFileforA");

         // I am returning data file name here but you can read data file and return proper data
         return new String[] {dataFilename};
      }

      case "B" :
      {
         String dataFilename= iTestContext.getCurrentXmlTest().getParameter("DataFileforB");
         return new String[] {dataFilename};
      }

      case "C" :
      {
         String dataFilename= iTestContext.getCurrentXmlTest().getParameter("DataFileforC");
         return new String[] {dataFilename};
      }

      default:
         return new String[] {"No Data File"};
      }

   }

   // A test method
   @Test(dataProvider = "DataContainer")
   public void A(String dataFilename)
   {
      System.out.println("DatafileName for method A is " + dataFilename);
   }

   // A test method
   @Test(dataProvider = "DataContainer")
   public void B(String dataFilename)
   {
      System.out.println("DatafileName for method B is " + dataFilename);
   }

   // A test method
   @Test(dataProvider = "DataContainer")
   public void C(String dataFilename) 
   {
      System.out.println("DatafileName for method C is " + dataFilename);
   }
}

TestNG Xml File:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
   <parameter name="DataFileforA" value="methodA.xlsx" />
   <parameter name="DataFileforB" value="methodB.xlsx" />
   <parameter name="DataFileforC" value="methodC.xlsx" />
   <test thread-count="5" name="Test">
      <classes>
         <class name="DataProvider.CustomDataProviderWithParameter"/>
      </classes>
   </test>
   <!-- Test -->
</suite>
<!-- Suite -->

Running the DataProvider Method in Parallel

We Saw that when the test method was executed, the execution order was the same as the data is passed from the dataprovider method. This is because in the @DataProvider annotated method, the attribute “parallel” is “false,” which is the default value.

If you want to run parallel, you need to set the “parallel=true” in the dataprovider method. Like below:

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderWithIndices 
{

   // You must need to mention data provider method name in Test method
   @Test(dataProvider="DataContainer")
   public void studentRegistration(String First_name, String Last_Name, String Email_ID) 
   {
      System.out.println("Registered student with details: "+First_name+" "+Last_Name+" "+Email_ID);

   }

   /*
    * Since we have set parallel as true, test method will be run in parallel for set of data provided by data provider.
    */
   @DataProvider(name="DataContainer", parallel=true)
   public Object[] myDataProvider() {

      Object data[][]= new Object[5][3];
      // First student details
      data[0][0]= "Mukesh";
      data[0][1]= "Otwani";
      data[0][2]= "Motwani@gmail.com";

      // Second student details
      data[1][0]= "Amod";
      data[1][1]= "Mahajan";
      data[1][2]= "amahajan@hotmail.com";

      // Third student details
      data[2][0]= "Animesh";
      data[2][1]= "Prashant";
      data[2][2]= "aprashant@gmail.com";

      // Fourth student details
      data[3][0]= "Ankur";
      data[3][1]= "Singh";
      data[3][2]= "asingh@gmail.com";

      // Fifth student details
      data[4][0]= "Amritansh";
      data[4][1]= "Kumar";
      data[4][2]= "akumar@gmail.com";

      return data;

   }
}

You can see that the console output is not in the data sequence provided by the DataProvider method. It means it was run parallel. Parallel data providers running from an XML file share the same pool of threads, which has a size of 10 by default. You can modify this value in the tag of your XML file:

<suite name=”Suite1″ data-provider-thread-count=”20″ >

Running Test for Specific Set of Data Provided by DataProvider Method

When running a testng class, a test method runs for all data sets returned by the dataprovider method because the dataProvider annotated method has an attribute called “indices,” and the default value for that is “all”.

But we can modify that indices value and choose which data set we want to run out of the linked test method.

For Example, A DataProvider test method has five sets of data; out of that, you want to run for 2 and 4. Then we can do that like the below program:

package DataProvider;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderWithIndices 
{

   // You must need to mention data provider method name in Test method
   @Test(dataProvider="DataContainer")
   public void studentRegistration(String First_name, String Last_Name, String Email_ID) 
   {
      System.out.println("Registered student with details: "+First_name+" "+Last_Name+" "+Email_ID);

   }

   // A data provider method with return type as 2D array
   // Data provider indices starts from zero.
   @DataProvider(name="DataContainer" , indices = {2,4})
   public Object[] myDataProvider() 
   {

      Object data[][]= new Object[5][3];
      // First student details
      data[0][0]= "Mukesh";
      data[0][1]= "Otwani";
      data[0][2]= "Motwani@gmail.com";

      // Second student details
      data[1][0]= "Amod";
      data[1][1]= "Mahajan";
      data[1][2]= "amahajan@hotmail.com";

      // Third student details
      data[2][0]= "Animesh";
      data[2][1]= "Prashant";
      data[2][2]= "aprashant@gmail.com";

      // Fourth student details
      data[3][0]= "Ankur";
      data[3][1]= "Singh";
      data[3][2]= "asingh@gmail.com";

      // Fifth student details
      data[4][0]= "Amritansh";
      data[4][1]= "Kumar";
      data[4][2]= "akumar@gmail.com";

      return data;

   }
}

This feature is not more helpful as you need to go and edit in the DataProvider method if you want to update indices.

This feature would have been more useful if :

  • Indices can be provided in Test annotated methods instead of the DataProvider annotated method. It would help in maintaining fewer DataProvider methods for multiple Test methods.
  • Indices are allowed to pass from testng xml.

Note:

  • The index starts from zero, and if the data set is not present for the mentioned index, no error or exception will be thrown.
  • Remember, Indices are provided in the DataProvider annotated method, not the Test annotated.

Read Excel Data Using DataProvider TestNG: How To Read Excel Data Using DataProvider TestNG Example Program?

package com.selenium.TestNG;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderExample1 
{
   String Data;
   @Test(dataProvider="wpdata")
   public void wordpresslogin(String Uname,String PWD) throws InterruptedException 
   {
      WebDriver driver=new FirefoxDriver();
      driver.manage().window().maximize();
      driver.get("https://www.odishaportals.in/wp-admin/");
      driver.manage().timeouts().implicitlyWait(10000, TimeUnit.SECONDS);
      driver.findElement(By.id("user_login")).sendKeys(Uname);
      driver.findElement(By.id("user_pass")).sendKeys(PWD);
      driver.findElement(By.id("wp-submit")).click();
      Thread.sleep(3000);
      driver.close();
   }
   @DataProvider(name="wpdata")
   public Object[][] wordpressdata()
   {
      Object[][] Data=new Object[3][2];
      Data [0][0]="test";
      Data [0][1]="test1";
      
      Data [1][0]="test2";
      Data [1][1]="test2";
      
      Data [2][0]="test";
      Data [2][1]="test";
      
      return Data;
   }
}

Avatar for Softwaretestingo Editorial Board

I love open-source technologies and am very passionate about software development. I like to share my knowledge with others, especially on technology that's why I have given all the examples as simple as possible to understand for beginners. All the code posted on my blog is developed, compiled, and tested in my development environment. If you find any mistakes or bugs, Please drop an email to softwaretestingo.com@gmail.com, or You can join me on Linkedin.

Leave a Comment