DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world
How To Exclude Methods In TestNG: IMethodSelector
How to exclude methods in TestNG ?
Details are in the javadoc associated with the code below: one java class: copy-paste it (in a 'test' package) and run it (Java5 or 6, TestNG 5.8). You need to add testng-jdk5.jar to your classpath.
This class is also written in response to a <a href="http://stackoverflow.com/questions/9044#275342"><b>StackOverflow Code Challenge</b></a>.
package test;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import org.testng.IMethodSelector;
import org.testng.IMethodSelectorContext;
import org.testng.ISuite;
import org.testng.ITestListener;
import org.testng.ITestNGMethod;
import org.testng.ITestRunnerFactory;
import org.testng.TestNG;
import org.testng.TestNGCommandLineArgs;
import org.testng.TestNGException;
import org.testng.TestRunner;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.testng.reporters.JUnitXMLReporter;
import org.testng.reporters.TestHTMLReporter;
import org.testng.xml.XmlMethodSelector;
import org.testng.xml.XmlTest;
/**
* Allow to mark all @Test annotated public method, and avoid un-annotated public methods. <br />
* In response to a <a href="http://stackoverflow.com/questions/9044#275342">code challenge</a> on StackOverflow, where the goal is to
* "specify a class wide group on a TestNG test case". <br />
* The @Test annotation on the base class is the righ move, BUT it would also includes public methods
* <i>not</i> annotated as Test methods. <p />
* This shows how to refine the Method lists by using a custom MethodSelector in TestNG (5.8).
* @author <a href="http://stackoverflow.com/users/6309/vonc">VonC</a>
*/
@Test(groups = { "aGlobalGroup" })
public class Base {
/**
*
*/
@BeforeClass
public final void setUp() {
System.out.println("Base class: @BeforeClass");
}
/**
* Test not part a 'aGlobalGroup', but still included in that group due to the class annotation. <br />
* Will be executed even if the TestNG class tested is a sub-class.
*/
@Test(groups = { "aLocalGroup" })
public final void aFastTest() {
System.out.println("Base class: Fast test");
}
/**
* Test not part a 'aGlobalGroup', but still included in that group due to the class annotation. <br />
* Will be executed even if the TestNG class tested is a sub-class.
*/
@Test(groups = { "aLocalGroup" })
public final void aSlowTest() {
System.out.println("Base class: Slow test");
//throw new IllegalArgumentException("oups");
}
/**
* Should not be executed. <br />
* Yet the global annotation Test on the class would include it in the TestNG methods...
*/
public final void notATest() {
System.out.println("Base class: NOT a test");
}
/**
* SubClass of a TestNG class. Some of its methods are TestNG methods, other are not. <br />
* The goal is to check if a group specify in the super-class will include methods of this class. <br />
* And to avoid including too much methods, such as public methods not intended to be TestNG methods.
* @author <a href="http://stackoverflow.com/users/6309/vonc">VonC</a>
*/
public static class Extended extends Base
{
/**
* Test not part a 'aGlobalGroup', but still included in that group due to the super-class annotation. <br />
* Will be executed even if the TestNG class tested is a sub-class.
*/
@Test
public final void anExtendedTest() {
System.out.println("Extended class: An Extended test");
}
/**
* Should not be executed. <br />
* Yet the global annotation Test on the class would include it in the TestNG methods...
*/
public final void notAnExtendedTest() {
System.out.println("Extended class: NOT an Extended test");
}
}
/**
* a TestRunnerFactory is needed to register a custom XmlMethodSelector to the XMLTest. <br />
* That XmlMethodSelector will be used to build a IMethodSelector
* @author <a href="http://stackoverflow.com/users/6309/vonc">VonC</a>
*/
public static class MyTestRunnerFactory implements ITestRunnerFactory
{
/**
* Register a custom XmlMethodSelector. <br />
* One with high priority, executed before other (default) XmlMethodSelector.
* @see org.testng.ITestRunnerFactory#newTestRunner(org.testng.ISuite, org.testng.xml.XmlTest)
*/
@Override
public final TestRunner newTestRunner(final ISuite aSuite, final XmlTest anXMLTest)
{
final XmlMethodSelector anXmlMethodSelector = new XmlMethodSelector();
anXmlMethodSelector.setName(MyMethodSelector.class.getName());
anXmlMethodSelector.setPriority(1);
anXMLTest.getMethodSelectors().add(anXmlMethodSelector);
// rest is a copy of the super() method.
final TestRunner runner = new TestRunner(aSuite, anXMLTest, false /*skipFailedInvocationCounts */);
runner.addListener(new TestHTMLReporter());
runner.addListener(new JUnitXMLReporter());
return runner;
}
}
/**
* Custom MethodSelector to avoid selecting non-annotated public methods. <br />
* That way, a global Test annotation at the class level does not parasite non-test methods.
* @author <a href="http://stackoverflow.com/users/6309/vonc">VonC</a>
*/
public static class MyMethodSelector implements IMethodSelector {
/**
* default serial ID.
*/
private static final long serialVersionUID = 1L;
/**
* Any public non-annotated method will be ignored by <i>all</i> MethodSelector. <br />
* Any other one will be ignore, but only be this MethodSelector. Other might select them.
* @see org.testng.IMethodSelector#includeMethod(org.testng.IMethodSelectorContext, org.testng.ITestNGMethod, boolean)
*/
@Override
public final boolean includeMethod(final IMethodSelectorContext context, final ITestNGMethod method,
final boolean isTestMethod)
{
System.out.println(method.getMethodName() + ": " + method.isTest() + "****** " + stringArrayToString(method.getGroups()));
boolean stop = true;
final Annotation[] someAnnotations = method.getMethod().getAnnotations();
for (int i = 0; i < someAnnotations.length; i++)
{
final Annotation anAnnotation = someAnnotations[i];
if(anAnnotation instanceof Test)
{
stop = false;
break;
}
}
//stop = false; // if this line is un-commented, all methods are taken into account
if(stop)
{
System.out.println("IGNORE AND EXCLUDE not-annotated public method '" + method.getMethodName() + "'");
context.setStopped(true);
}
return false;
}
private static String stringArrayToString(final String[] aStringArray)
{
final StringBuilder msg = new StringBuilder("[");
if(aStringArray != null)
{
for (int i = 0; i < aStringArray.length; i++) {
msg.append(aStringArray[i]);
if(i<(aStringArray.length-1))
{
msg.append(", ");
}
}
}
msg.append("]");
return msg.toString();
}
private List<ITestNGMethod> testMethods;
/**
* @see org.testng.IMethodSelector#setTestMethods(java.util.List)
*/
@Override
public final void setTestMethods(final List<ITestNGMethod> someTestMethods)
{
this.testMethods = someTestMethods;
System.out.println(this.testMethods);
}
}
/**
* Custom TestNG needed because the original TestNG does not interpret the -testrunfactory option. <br />
* That bug has been <a href="http://forums.opensymphony.com/thread.jspa%3FmessageID%3D112438%26%23112402">reported
* two years ago</a>...
* @author <a href="http://stackoverflow.com/users/6309/vonc">VonC</a>
*/
public static class MyTestNG extends TestNG
{
/**
* The TestNG entry point for command line execution. <br />
* Execute a TestNG on Extended class for the group 'aGlobalGroup'.
* @param argv the TestNG command line parameters.
*/
public static void main(final String[] argv) {
System.out.println("77743332111".replaceAll("(\\d)\\1+", "$1"));
final String[] myArgv = new String[] {
"-groups", "aGlobalGroup",
"-testclass", "test.Base$Extended",
"-suitename", "TestNG tests",
"-testname", "Group Inheritance",
"-testrunfactory", "test.Base$MyTestRunnerFactory",
"-log", "10"
};
final TestNG testng = privateMain(myArgv, null);
System.exit(testng.getStatus());
}
/**
* Make sure that MyTestNG is created by the main TESTNG function. <br />
* That way, {@link MyTestNG#configure(Map)} is called, and tesntgfactory is taken into account.
* @param argv
* @param listener
* @return MyTestNG instance, with a TestRunnerFactory created if specified.
*/
@SuppressWarnings("unchecked")
public static TestNG privateMain(final String[] argv, final ITestListener listener)
{
final Map arguments= checkConditions(TestNGCommandLineArgs.parseCommandLine(argv));
final MyTestNG result = new MyTestNG();
if (null != listener) {
result.addListener(listener);
}
result.configure(arguments);
try {
result.run();
}
catch(final TestNGException ex)
{
if (TestRunner.getVerbose() > 1)
{
ex.printStackTrace(System.out);
}
else {
System.err.println("[ERROR]: " + ex.getMessage());
}
result.setStatus(HAS_FAILURE);
}
return result;
}
@Override
protected final void setStatus(final int status) {
super.setStatus(status);
}
/**
* Configure the TestNG instance by reading the settings provided in the map. <br />
* Takes into the TESTRUNNER_FACTORY_COMMAND_OPT, which is still ignored
* even <a href="http://forums.opensymphony.com/thread.jspa?messageID=112438%26%23112402">after being
* reported missing</a> to Cedric BEUST.
* @param cmdLineArgs map of settings
* @see TestNGCommandLineArgs for setting keys
*/
@Override
@SuppressWarnings({"unchecked"})
public final void configure(final Map cmdLineArgs)
{
super.configure(cmdLineArgs);
final Class aTestRunnerFactoryClass = (Class) cmdLineArgs.get(TestNGCommandLineArgs.TESTRUNNER_FACTORY_COMMAND_OPT);
if(aTestRunnerFactoryClass != null)
{
setTestRunnerFactoryClass(aTestRunnerFactoryClass);
}
}
}
/**
* Redirection to the main of MyTestNG. <br />
* All TestNG parameters are hard-coded there.
* @param argv
*/
public static void main(final String[] argv) {
MyTestNG.main(argv);
}
}





