0

I've implemented dataProvider and it works for each class, but it doesn't alternate between test cases.

What it currently does is: start with test case 1 and go through all the dataset in the dataProvider, then move to Test Case 2 and do the same with all the data set.

I would like instead to alternate between test cases:

  • Start with line 1 of dataProvider: Test case 1 with line 1 of dataset, then move to Test Case 2 with line 1 of dataProvider, then Test case 3 with line 1 of dataProvider ... Once all test cases done, then

  • Move to the line 2 of dataProvider and start again: Test Case 1 with line 2 of dataProvider, then Test Case 2 with line 2 of dataProvider.... Is this possible ?

theai.1
  • 21
  • 4

1 Answers1

0

Are you looking for class-level DataProvider?

In order to parametrize the test class, use @Factory + @DataProvider in the next way:

import org.testng.annotations.DataProvider
import org.testng.annotations.Factory
import org.testng.annotations.Test

class MyTestClass {

    String arg1;
    String arg2;

    @Factory(dataProvider = "provideClassArgs")
    public MyTestClass(String arg1, String arg2) {
        this.arg1 = arg1;
        this.arg2 = arg2;
    }

    @DataProvider
    public static Object[][] provideClassArgs() {
        return new Object[]{
                {"arg1-value1", "arg2-value1"},
                {"arg1-value2", "arg2-value2"}

        };
    }

    @Test
    public void test1() {
        System.out.println("Do test1 with: " + arg1 + ", " + arg2);
    }

    @Test
    public void test2() {
        System.out.println("Do test2 with: " + arg1 + ", " + arg2);
    }

    @Test
    public void test3() {
        System.out.println("Do test3 with: " + arg1 + ", " + arg2);
    }
}

with the next textng.xml

NOTE: group-by-instances="true" should be defined

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Test Suite" group-by-instances="true">
    <test name="Test">
        <classes>
            <class name="samples.MyTestClass"/>
        </classes>
    </test>
</suite>

you will get:

Do test1 with: arg1-value2, arg2-value2
Do test2 with: arg1-value2, arg2-value2
Do test3 with: arg1-value2, arg2-value2
Do test1 with: arg1-value1, arg2-value1
Do test2 with: arg1-value1, arg2-value1
Do test3 with: arg1-value1, arg2-value1

Class Execution Order

By default, DataProvider items will launch classes in random order.

The easiest way to make the order fixed: is override toString() method for test-class.

For this example:

@Override
public String toString() {
    return "MyTestClass";
}

tests will be sorted alphabetically based on DataProvider args, converted to a string. This won't be the natural order like in DataProvider, but at least not random.

See more:


Use Factory with DataProvider for multiple classes

NOTE: the request is to run all the classes per args line, and then the same classes per next arg line, etc.

  1. Create the parent class for all test classes.

    • It should define all the required arguments
    • It should have a constructor for all the arguments
    • It should override toString() method, the result to be reflected by all the args. This is valuable for the execution order.
public class MyClassParent {

  String arg1;
  String arg2;

  public MyClassParent(String arg1, String arg2) {
      this.arg1 = arg1;
      this.arg2 = arg2;
  }

  @Override
  public String toString() {
      arg1.toString() + arg2.toString();
  }
}
  1. Implement test classes, like:

    • Extend from MyClassParent
    • Create the same constructor
public class MyTestClass1 extends MyClassParent {

    public MyTestClass1(String arg1, String arg2) {
        super(arg1, arg2);
    }

    @Test
    public void test1() {
        System.out.println("MyTestClass1: Do test1 with: " + arg1 + ", " + arg2);
    }

}
  1. Implement Factory with DataProvider.

Factory with DataProvider moved to separate class and produces test class objects array.

  • For applying the correct ordering IMethodInterceptor TestNG Listener interface was implemented.
  • And applied for the class with @Listeners(MyFactory).
  • intercept method overridden and sorting applied here.
  • Custom Comparator implemented for sorting, which compare 2 MyClassParent based on it's toString() results.

So while toString() depends on the MyClassParent args, all the classes for the same args will be in the same group.

import org.testng.IMethodInstance
import org.testng.IMethodInterceptor
import org.testng.ITestContext
import org.testng.annotations.DataProvider
import org.testng.annotations.Factory
import org.testng.annotations.Listeners

@Listeners(MyFactory)
public class MyFactory implements IMethodInterceptor {

    @Factory(dataProvider = "provideClassArgs")
    public Object[] generateTests(String arg1, String arg2) {
        return new Object[] {
                new MyTestClass1(arg1, arg2),
                new MyTestClass2(arg1, arg2),
                new MyTestClass3(arg1, arg2),
                new MyTestClass4(arg1, arg2),
                new MyTestClass5(arg1, arg2)
        };
    }

    @DataProvider
    public static Object[][] provideClassArgs() {
        return new Object[]{
                {"arg1-value1", "arg2-value1"},
                {"arg1-value2", "arg2-value2"},
                {"arg1-value3", "arg2-value2"}

        };
    }

    @Override
    List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
        // applying sorting is essential to change the tests order
        methods.sort(new MyTestClassComparator());
        return methods;
    }

    // internal class added, assumed it should compare only methods from MainImpClass
    // here compareTo delegated to getA() results
    class MyTestClassComparator implements Comparator<IMethodInstance> {

        @Override
        int compare(IMethodInstance o1, IMethodInstance o2) {
            return ((MyClassParent)o1.getInstance()).toString().compareTo(((MyClassParent)o2.getInstance()).toString());
        }
    }
}

4 TestNG xml:

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Test Suite" group-by-instances="true">
    <test name="Test">
        <classes>
            <class name="samples.MyFactory"/>
        </classes>
    </test>
</suite>

The final output:

MyTestClass1: Do test1 with: arg1-value1, arg2-value1
MyTestClass2: Do test2 with: arg1-value1, arg2-value1
MyTestClass3: Do test3 with: arg1-value1, arg2-value1
MyTestClass4: Do test4 with: arg1-value1, arg2-value1
MyTestClass5: Do test5 with: arg1-value1, arg2-value1
MyTestClass1: Do test1 with: arg1-value2, arg2-value2
MyTestClass2: Do test2 with: arg1-value2, arg2-value2
MyTestClass3: Do test3 with: arg1-value2, arg2-value2
MyTestClass4: Do test4 with: arg1-value2, arg2-value2
MyTestClass5: Do test5 with: arg1-value2, arg2-value2
MyTestClass1: Do test1 with: arg1-value3, arg2-value2
MyTestClass2: Do test2 with: arg1-value3, arg2-value2
MyTestClass3: Do test3 with: arg1-value3, arg2-value2
MyTestClass4: Do test4 with: arg1-value3, arg2-value2
MyTestClass5: Do test5 with: arg1-value3, arg2-value2
Max Daroshchanka
  • 2,698
  • 2
  • 10
  • 14
  • Hi, thank you for the answer. This works, however, in my case, my Test cases are each on a separate class. In fact, each test case = test class. Do you know how I can do this, but with multiple test classes ? Thank you – theai.1 Feb 13 '22 at 16:20
  • Yes, I'll try to help you tomorrow by my timezone. I have one idea. So, is it right, that you have a multiple classes and one test method per class, and you want to launch a bunch of classes with some parameters set, and then the same with another parameters set? – Max Daroshchanka Feb 13 '22 at 16:52
  • @walidsabir I've updated the answer with `Use Factory with DataProvider for multiple classes` section. Take a look. – Max Daroshchanka Feb 14 '22 at 07:10
  • HI Max, thank you very much for the reply. I tried this, however, it ends up just launching the whole data set from the DataProvider for each class before moving to the next. It does not alternate classes for each 'line' of the data set. – theai.1 Feb 15 '22 at 07:19
  • @walidsabir this should be executed via `testng.xml`, and `group-by-instances="true"` should be defined. Have you tried this way? I'll recheck. – Max Daroshchanka Feb 15 '22 at 07:22
  • @walidsabir I see, It does not alternate classes. I'll add some updates. – Max Daroshchanka Feb 15 '22 at 07:49
  • @walidsabir I've updated the answer, please, try again. – Max Daroshchanka Feb 15 '22 at 08:22
  • 1
    Hi Max, thank you very much for all the work. I'll take a loo at it and try to understand it, since I am still a beginner to TestNG. I will get back to you if I have any questions. Thank you! – theai.1 Feb 20 '22 at 08:30