Method interceptor in TestNG ( To reorder test methods )

In this post, we will be discussing how to use the method interceptors in TestNG. TestNG runs our test cases in alphabetical order of their method names. If TestNG has decided the order in which the test methods will be invoked, then we can split it into two groups –

  • The method runs in a particular order – These are the test methods with dependencies on another method ( most likely, they will be using dependsOnMethod or dependsOnGroups ). So, the dependent method will run before the depending method.
  • The method runs in no particular orderThe methods that don’t belong to the first category come in this one. To give more control over the methods falling in this category, TestNG provided us with Method interceptors.
public interface IMethodInterceptor extends ITestNGListener {

  List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context);
}

What does the “intercept” method do? It will accept a list of methods in its parameters that can be run in any order and return a similar list of methods.
What does the “intercept” method return? It will return a similar list of methods that were passed in its parameters, and the returned list can be –

  • The same list, we received in the parameter but in a different order.
  • A smaller list of IMethodInstance methods.
  • A larger list of IMethodInstance methods than what was received in the parameters.

Let’s first take a sample test class in which we will try to use the method interceptors to change the ordering of the test methods.

CodekruTest.java

package Test;

import org.testng.Assert;
import org.testng.annotations.Test;

public class CodekruTest {

	@Test
	public void gammaMethod() {
		System.out.println("Executing gammaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

	@Test
	public void alphaMethod() {
		System.out.println("Executing alphaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

	@Test
	public void betaMethod() {
		System.out.println("Executing betaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

	@Test
	public void deltaMethod() {
		System.out.println("Executing deltaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

}

Below is the XML file used to run the above test class.

<suite name="codekru">

	<test name="codekruTest">
		<classes>
			<class name="Test.CodekruTest">
			</class>
		</classes>
	</test>
</suite>

Output after running the XML file-

Executing alphaMethod in CodekruTest class
Executing betaMethod in CodekruTest class
Executing deltaMethod in CodekruTest class
Executing gammaMethod in CodekruTest class

The method was executed in the alphabetical order of their test method names. If you want to know more about the ordering, we recommend reading this article.

What should we do so that a particular method ( say, deltaMethod in this case ) always runs first?

As we want to run a particular method first. We can do that in multiple ways –

  • By assigning a priority to that method so that it always runs first. This would be very easy to do, but that class might include some more test methods, and some of the other test methods might also need some priority. Executing a method using priority is a good way if we pass priority as a highly negative value ( like -12345). This value shouldn’t interfere with any of the priorities of other test methods that might be included in the future ( Go to this link if you want to learn about priority ).
  • The second way is to use the method interceptor. Method interceptor provides an excellent interface to run our tests in the order we want.

Below is the class that will implement the IMethodInterceptor interface and executes the deltaMethod first.

package Test;

import java.util.ArrayList;
import java.util.List;

import org.testng.IMethodInstance;
import org.testng.IMethodInterceptor;
import org.testng.ITestContext;

public class CodekruMethodInterceptor implements IMethodInterceptor {

	public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
		List<IMethodInstance> result = new ArrayList<IMethodInstance>();
		for (IMethodInstance m : methods) {
			if (m.getMethod().getMethodName().equalsIgnoreCase("deltaMethod")) {
				result.add(0, m);
			} else {
				result.add(m);
			}

		}
		return result;
	}

}

Now, we have to add the above class as a listener in the XML file, as shown below.

<suite name="codekru">
	<listeners>
		<listener class-name="Test.CodekruMethodInterceptor" />
	</listeners>
	<test name="codekruTest">
		<classes>
			<class name="Test.CodekruTest">
			</class>
		</classes>
	</test>
</suite>

Output after running the XML file-

Executing deltaMethod in CodekruTest class
Executing alphaMethod in CodekruTest class
Executing betaMethod in CodekruTest class
Executing gammaMethod in CodekruTest class

Here we can see that the deltaMethod executed first out of all of the test methods.

Why did we say that method interceptors work with methods that run in no particular order?

Let’s try to use this on methods that run in a particular order ( methods depending on other methods ). Below is our CodekruTest class, but we have modified it to run the methods in a specific order.

package Test;

import org.testng.Assert;
import org.testng.annotations.Test;

public class CodekruTest {

	@Test(dependsOnMethods = {"betaMethod"})
	public void gammaMethod() {
		System.out.println("Executing gammaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

	@Test
	public void alphaMethod() {
		System.out.println("Executing alphaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

	@Test(dependsOnMethods = {"alphaMethod"})
	public void betaMethod() {
		System.out.println("Executing betaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

	@Test(dependsOnMethods = {"gammaMethod"})
	public void deltaMethod() {
		System.out.println("Executing deltaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

}

Now, again the same XML file.

<suite name="codekru">
	<listeners>
		<listener class-name="Test.CodekruMethodInterceptor" />
	</listeners>
	<test name="codekruTest">
		<classes>
			<class name="Test.CodekruTest">
			</class>
		</classes>
	</test>
</suite>

Output after running the XML file –

Executing alphaMethod in CodekruTest class
Executing betaMethod in CodekruTest class
Executing gammaMethod in CodekruTest class
Executing deltaMethod in CodekruTest class

We can see here that the deltaMethod was executed at the very last because it depended on some other method which further depended on some method. Hence, that order was followed in the execution of cases.

What if we wanted to run a particular group first?

We will be picking the code from the TestNG documentation, but at the time of writing this article, there is a minor issue in the code in TestNG documentation. So, we will be writing a fully functional code here.

Let’s again take our CodekruTest class

package Test;

import org.testng.Assert;
import org.testng.annotations.Test;

public class CodekruTest {

	@Test(groups = { "func" })
	public void gammaMethod() {
		System.out.println("Executing gammaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

	@Test
	public void alphaMethod() {
		System.out.println("Executing alphaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

	@Test(groups = { "func" })
	public void betaMethod() {
		System.out.println("Executing betaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

	@Test
	public void deltaMethod() {
		System.out.println("Executing deltaMethod in CodekruTest class");
		Assert.assertTrue(true);
	}

}

Here we want to run the methods that belong to the group “func” first, and the rest of the methods should be executed afterward. Below is our method interceptor code for the same.

package Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.testng.IMethodInstance;
import org.testng.IMethodInterceptor;
import org.testng.ITestContext;
import org.testng.annotations.Test;

public class CodekruMethodInterceptor implements IMethodInterceptor {

	public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
		List<IMethodInstance> result = new ArrayList<IMethodInstance>();
		for (IMethodInstance m : methods) {
			Test test = m.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class);
			Set<String> groups = new TreeSet<String>();
			
			for (String group : test.groups()) {
				groups.add(group);
			}

			// if group is "func", then put the method
			// at the first position in the list
			if (groups.contains("func")) {
				result.add(0, m);
			} else {
				result.add(m);
			}
		}
		return result;

	}

}

Below is the XML used to execute the CodekruTest class, where the test methods belonging to the func will be executed first.

<suite name="codekru">
	<listeners>
		<listener class-name="Test.CodekruMethodInterceptor" />
	</listeners>
	<test name="codekruTest">
		<classes>
			<class name="Test.CodekruTest">
			</class>
		</classes>
	</test>
</suite>

Output after running the above XML file –

Executing gammaMethod in CodekruTest class
Executing betaMethod in CodekruTest class
Executing alphaMethod in CodekruTest class
Executing deltaMethod in CodekruTest class

Here we can see that the gammaMethod and the betaMethod were executed first because they belonged to the “func” group.

There are many uses of the method interceptors in TestNG. We hope that you have liked the article. If you have any doubts or concerns, please feel free to write us in the comments or mail us at admin@codekru.com.

Reference – https://testng.org/doc/index.html

Liked the article? Share this on

Leave a Comment

Your email address will not be published. Required fields are marked *