javatestngtestng.xmltestng-annotation-test

Only the last @Test is being executed when using IMethodInterceptor as Listener in TestNG


I'm trying to use @Factory to create multiple instances of a class and print the values in the same order that we have passed by using IMethodInterceptor listener:

public class MainFactoryClass implements IMethodInterceptor {

@Factory
public Object[] mainFactory() {

    Object[] data = new Object[6];
    data[0] = new MainImpClass(9);
    data[1] = new MainImpClass(10);
    data[2] = new MainImpClass(11);
    data[3] = new MainImpClass(12);
    data[4] = new MainImpClass(13);
    data[5] = new MainImpClass(14);
    return data;
}

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

    Map<Integer, IMethodInstance> orders = new TreeMap<>();

    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()) {
        IMethodInstance test = orders.get(order);
        orderList.add(test);        
    }
    return orderList;
}

}

If I have 5 @Test methods to print the values in the class, it only takes the last @Test method into account and prints the value. What am I doing wrong?. i.e. getValue1, getValue2, getValue3, getValue4 are not run.

Tried to use priority or dependsOnMethods. Code mentioned below:

@Listeners({ MainFactoryClass.class })

public class MainImpClass {
int a;

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

public int getA() {
    return a;
}

@Test(priority = 0)
public void getValue1() {
    System.out.println("Value from getValue1: " + a);
}

@Test(priority = 1)
public void getValue2() {
    System.out.println("Value from getValue2: " + a);
}

@Test(priority = 2)
public void getValue3() {
    System.out.println("Value from getValue3: " + a);
}

@Test(priority = 3)
public void getValue4() {
    System.out.println("Value from getValue4: " + a);
}

@Test(priority = 4)
public void getValue5() {
    System.out.println("Value from getValue5: " + a);
}

}

The output is in an order as we have created the instances but only from last @Test.

Value from getValue5: 9
Value from getValue5: 10
Value from getValue5: 11
Value from getValue5: 12
Value from getValue5: 13
Value from getValue5: 14

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 -->

Solution

  • Currently your configuration is such the there are 6 test instances and each contains 5 methods each. So a total of these 30 methods would be passed onto the intercept method. So since you are grouping them using getA, only one method (last) among the 5 methods would be finally available in the map for an instance. One solution would be to implement @Max's solution by keeping a map of lists.

    Another solution, assuming that you are using Java 8+, you could simplify the intercept method by using sorting instead of creating a map.

    Comparator<IMethodInstance> customCompare = 
       Comparator.comparing(inst -> ((MainImpClass) inst.getInstance()).getA())
                 .thenComparing(inst -> inst.getMethod().getPriority());
        
    // Now sort the list
    list.sort(customCompare);
    return list;
    

    Sometimes, this might show some compile error, in which case you can use:

    Comparator<Object> customCompare = 
       Comparator.comparing(inst -> ((MainImpClass) ((IMethodInstance) inst).getInstance()).getA())
                 .thenComparing(inst -> ((IMethodInstance) inst).getMethod().getPriority());
    

    Note: Better to use separate classes for factory and interceptor as both are doing different things. Iterate the map using entrySet() and use entry.getValue() instead of iterating with keySet() and accessing value by map.get(key). (Refer)