Surendra Anand
Surendra Anand

Reputation: 121

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

Upvotes: 2

Views: 199

Answers (2)

Gautham M
Gautham M

Reputation: 4935

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)

Upvotes: 1

Max Daroshchanka
Max Daroshchanka

Reputation: 2978

Your intercept method works in the way, when you try to put multiple values with the same key to the map, so only the last value saved due to keys duplication.

So you see the last test per class..

The issue is here:

orders.put(Integer.valueOf(testData.getA()), instance);

The solution here might be

Map<Integer, List<IMethodInstance>> orders

append the List in the loop.

And by the end iterate over the keyset and make a single list from all the values.


Implement sorting with IMethodInterceptor

To change the order of the test you have to apply some sorting logic.

Here in your code, there is no sorting applied. You've created a new list, but the order was not changed.

I'll add an example, how to apply sorting by MainImpClass getA() method result using custom Comparator. I'll add the implementation in MainFactoryClass as you have currently, but usually I split factories and listeners to separate classes.

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
    List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
        // applying sorting is essential to change the tests order
        methods.sort(new MainImpClassComparator());
        return methods;
    }

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

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


Upvotes: 1

Related Questions