Stan Kurilin
Stan Kurilin

Reputation: 15792

Retrieve test name on TestNG

Can I retrieve currently running test name like in JUnit (using getName() or rules)?

@Test
public void fooBar(){
     System.out.println(magic()); //should print "fooBar"
}

P.S. I don't want use some self-written tool based on stack traces.

Upvotes: 23

Views: 52380

Answers (6)

David M
David M

Reputation: 11

Easiest way is to use Reporter.getCurrentTestResult().getName();. This gives you the name of the currently running test.

This is especially useful when you want to retrieve the test name from some common utility or logging class and don't want to force all your tests to pass the name.

Upvotes: 0

dimo414
dimo414

Reputation: 48794

You need to be careful when holding onto the values passed into listeners like IInvokedMethodListener as a naive implementation (including those in existing answers) will not be thread-safe. Since TestNG can run tests concurrently it's possible to see the stored value from a different test's listener. Here's an example with two tests, testA() and testB():

  1. beforeInvocation(testA) stores testA
  2. beforeInvocation(testB) stores testB overwriting testA
  3. testA() retrieves testB (!!)
  4. testB() retrieves testB

The TestMethodCapture class below handles this race condition correctly by associating the listener and its test via a ThreadLocal, ensuring that concurrently running tests will not overwrite each other.

Even better, it's not limited to just retrieving the test's name, it holds a reference to both the ITestNGMethod and ITestResult instances associated with the current test, so you can also inspect the method's class, test groups, and parameters.

You can use it like so:

@Listeners(TestMethodCapture.class)
public class TestMethodCaptureTest {
  @Test
  public void fooBar() {
    // will print "fooBar"
    System.out.println(TestMethodCapture.getTestMethod().getMethodName());
  }
}

And here's the class itself:

/**
 * Captures the currently executing test method so it can be accessed by the test,
 * e.g. to retrieve the test method's name. This class is thread-safe.
 *
 * <p>Register this class as a
 * <a href="http://testng.org/doc/documentation-main.html#testng-listeners">TestNG
 * listener</a>, then access the method and result from test code with the static
 * {@link #getTestMethod} and {@link #getTestResult} methods.
 * 
 * <p>Annotating a test class with {@code @Listeners(TestMethodCapture.class)} is the
 * suggested way to enable capturing if your test's correctness will depend on this
 * listener being enabled.
 */
public class TestMethodCapture implements IInvokedMethodListener {
  private static ThreadLocal<ITestNGMethod> currentMethods = new ThreadLocal<>();
  private static ThreadLocal<ITestResult> currentResults = new ThreadLocal<>();

  @Override
  public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
    currentMethods.set(method.getTestMethod());
    currentResults.set(testResult);
  }

  @Override
  public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
    currentMethods.remove();
    currentResults.remove();
  }

  public static ITestNGMethod getTestMethod() {
    return checkNotNull(currentMethods.get(),
      "Did you forget to register the %s listener?", TestMethodCapture.class.getName());
  }

  /**
   * Parameters passed from a data provider are accessible in the test result.
   */
  public static ITestResult getTestResult() {
    return checkNotNull(currentResults.get(),
      "Did you forget to register the %s listener?", TestMethodCapture.class.getName());
  }
}

If you aren't using Guava (why not??) you can add a checkNotNUll() method like this to make this compile:

private static <T> T checkNotNull(T o, String msg, Object param) {
  if (o == null) {
    throw new NullPointerException(String.format(msg, param));
  }
  return o;
}

Upvotes: 6

Tomasz Przybylski
Tomasz Przybylski

Reputation: 495

When you use TestNG you can use @BeforeTest annotation

Try set test name in testng.xml file test tag:

<test name="Check name test" >

and use this metod:

@BeforeTest
public void startTest(final ITestContext testContext) {
    System.out.println(testContext.getName()); // it prints "Check name test"
}

Upvotes: 15

Dmitry
Dmitry

Reputation: 2993

I found better solution with @BeforeMethod annotation:

import java.lang.reflect.Method;

public class Test
{ 

    @BeforeMethod
    public void handleTestMethodName(Method method)
    {
        String testName = method.getName(); 
        ...
    }

    ...
}

(based on solution from this thread)

Upvotes: 62

Cedric Beust
Cedric Beust

Reputation: 15608

Declare an ITestContext in parameter in your method and grab whatever information you need from it.

Upvotes: 9

Chris R
Chris R

Reputation: 199

According the to TestNG documentation at: http://testng.org/doc/documentation-main.html you can implement listeners that might be able to help you with your problem.

Look at section 5.16 TestNG Listeners, and in particular the IInvokedMethodListener (javadoc: http://testng.org/javadocs/org/testng/IInvokedMethodListener.html). You can hook into the beforeInvocation to grab the method name, hold onto it somewhere, and then use it in your test. You could of course, just use the the details immediately in your listener implementation.

Upvotes: 6

Related Questions