1

I try to use the @Factory to run the same Class with multiple instances. Please be noted that I'm a beginner to TestNG.

For e.g:

Implementation of @Factory:

public class MainFactoryClass{
@Factory
public Object[] mainFactory() {

    Object[] data = new Object[3];

    data[0] = new MainImpClass(9);

    data[1] = new MainImpClass(10);

    data[2] = new MainImpClass(11);

    return data;
}
}

And the main class:

public class MainImpClass {

int a;

public MainImpClass(int a) {
    this.a = a;
}


@Test
public void getValue1() {   

    System.out.println("Value from getValue1: " + a);

}

@Test
public void getValue2() {

    System.out.println("Value from getValue2: " + a);

}

@Test
public void getValue3() {

    System.out.println("Value from getValue3: " + a);

}

}

Actual:

Value from getValue1: 9
Value from getValue2: 9
Value from getValue3: 9
Value from getValue1: 11
Value from getValue2: 11
Value from getValue3: 11
Value from getValue1: 10
Value from getValue2: 10
Value from getValue3: 10

testng.xml:

  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
  <suite name="Factory Suite">
  <test thread-count="5" name=" Factory Test" group-by- 
  instances="true">
  <classes>
  <class name="com.trial.MainFactoryClass"/>
  </classes>
  </test> <!-- Test -->
  </suite> <!-- Suite -->

Updated - Implementing the IMethodInterceptor:

 @Override
 public List<IMethodInstance> intercept(List<IMethodInstance> list, 
 ITestContext iTestContext) {

    Map<Integer, IMethodInstance> orders = new TreeMap<>(); // 
  Ordered MAP
    
    for (IMethodInstance instance : list) {
        MainImpClass testData = (MainImpClass) 
    instance.getInstance();
        orders.put(Integer.valueOf(testData.getA()), instance);

        
    }
    
    List<IMethodInstance> orderList = new 
    ArrayList<IMethodInstance>(list.size());
    
    
    for (Integer order : orders.keySet()) { // rearrange
        IMethodInstance test = orders.get(order);
        orderList.add(test);

    }
            return orderList; // TestNG will execute in the order  
    return List

    }

When I try to run it as TestNG, The results are not in the same order we passed from Factory. How do we ensure that the output is same as how we passed the values in?.

1 Answers1

2

If no other order options have been defined, test methods ordered by its

<test-method-instance> name + <test-class-instance>.toString()

Since test-class.toString() always has some random part like MainImpClass@28a0fd6c, the default execution order is accidental.

The simplest way to streamline the execution order is to override the test-class toString() method like:

@Override
public String toString() {
    return "MainImpClass" + a;
}

But this does not answer your question about how to order tests in the order, provided by Factory.

How to keep the Factory-defined order

Note: this is not a generic solution, just for this simple context.

TestClass

Added private order variable which referred in toString().

public class MainImpClass {

    int a;

    private int order = 0;

    public MainImpClass(int a) {
        this.a = a;
    }

    @Override
    public String toString() {
        return "MainImpClass" + order;
    }

    void setOrder(int order) {
        this.order = order;
    }

    // ... rest of class

Factory Class

Added groupByOrder method which sets the order based on the class occurrence in the array.

public class MainFactoryClass {
    @Factory
    public Object[] mainFactory() {

        Object[] data = new Object[3];

        data[0] = new MainImpClass(9);

        data[1] = new MainImpClass(10);

        data[2] = new MainImpClass(11);

        return groupByOrder(data);
    }

    private static Object[] groupByOrder(Object[] data) {
        for (int i = 0; i < data.length; i++) {
            ((MainImpClass) data[i]).setOrder(i);
        }
        return data;
    }
}

Output

Value from getValue1: 9
Value from getValue1: 10
Value from getValue1: 11
Value from getValue2: 9
Value from getValue2: 10
Value from getValue2: 11
Value from getValue3: 9
Value from getValue3: 10
Value from getValue3: 11

Output with applied group-by-instances="true" in testng.xml

Value from getValue1: 9
Value from getValue2: 9
Value from getValue3: 9
Value from getValue1: 10
Value from getValue2: 10
Value from getValue3: 10
Value from getValue1: 11
Value from getValue2: 11
Value from getValue3: 11

How to improve

Some more generic solutions might be implemented with IMethodInterceptor TestNG listener.

class TestsOrderInterceptor implements IMethodInterceptor {

    @Override
    List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
        // sort by some rule
    }
}

and apply the listener

@Listeners(TestsOrderInterceptor.class)
public class MainFactoryClass {

or in XML suite

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="factory-tests">
    <listeners>
        <listener class-name="TestsOrderInterceptor"/>
    </listeners>
    ...

Max Daroshchanka
  • 2,698
  • 2
  • 10
  • 14
  • Thank you for your response. I tried to implement the IMethodInterceptor. But it only executes the last @Test and the order it executes issue is fixed. What am I missing?. Updated in the post – Surendra Anand Jan 28 '22 at 05:55