22

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.

Community
  • 1
  • 1
Stan Kurilin
  • 15,614
  • 21
  • 81
  • 132

5 Answers5

60

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)

Dmitry
  • 2,943
  • 1
  • 23
  • 26
15

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"
}
9

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

Cedric Beust
  • 15,480
  • 2
  • 55
  • 55
  • Actually I can't find it in this interface context/suite/currentXmlTest name doesn't contain this information. – Stan Kurilin Dec 22 '11 at 14:02
  • 3
    Your question is more a Java question than a TestNG one, and since you don't want to use the only way that I know to do it (walk the stack trace), I'm not sure what else to say... – Cedric Beust Dec 23 '11 at 16:27
6

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;
}
dimo414
  • 47,227
  • 18
  • 148
  • 244
  • Can you please explain the checkNotNull method that is being returned? Should we define the method? It shows an error that this method is not defined. – nivasan89 Jul 25 '16 at 10:04
  • 1
    @nivasan89 sorry I missed your comment. [`checkNotNull()`](https://github.com/google/guava/wiki/PreconditionsExplained) is coming from [Guava](https://github.com/google/guava). I would strongly encourage using this library in any Java project, but this method is essentially a nice wrapper around `if (foo == null) throw NullPointerException();` so you could just replace these calls with a similar conditional. – dimo414 Dec 27 '16 at 18:02
6

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.

Chris R
  • 199
  • 2
  • 2