Get name of currently executing test in JUnit 4

JavaUnit TestingJunit

Java Problem Overview


In JUnit 3, I could get the name of the currently running test like this:

public class MyTest extends TestCase
{
    public void testSomething()
    {
        System.out.println("Current test is " + getName());
        ...
    }
}

which would print "Current test is testSomething".

Is there any out-of-the-box or simple way to do this in JUnit 4?

Background: Obviously, I don't want to just print the name of the test. I want to load test-specific data that is stored in a resource with the same name as the test. You know, convention over configuration and all that.

Java Solutions


Solution 1 - Java

JUnit 4.7 added this feature it seems using TestName-Rule. Looks like this will get you the method name:

import org.junit.Rule;

public class NameRuleTest {
	@Rule public TestName name = new TestName();
	
	@Test public void testA() {
		assertEquals("testA", name.getMethodName());
	}
	
	@Test public void testB() {
		assertEquals("testB", name.getMethodName());
	}
}

Solution 2 - Java

JUnit 4.9.x and higher

Since JUnit 4.9, the TestWatchman class has been deprecated in favour of the TestWatcher class, which has invocation:

@Rule
public TestRule watcher = new TestWatcher() {
   protected void starting(Description description) {
      System.out.println("Starting test: " + description.getMethodName());
   }
};

Note: The containing class must be declared public.

JUnit 4.7.x - 4.8.x

The following approach will print method names for all tests in a class:

@Rule
public MethodRule watchman = new TestWatchman() {
   public void starting(FrameworkMethod method) {
      System.out.println("Starting test: " + method.getName());
   }
};

Solution 3 - Java

JUnit 5 and higher

In JUnit 5 you can inject TestInfo which simplifies test metadata injection to test methods. For example:

@Test
@DisplayName("This is my test")
@Tag("It is my tag")
void test1(TestInfo testInfo) {
    assertEquals("This is my test", testInfo.getDisplayName());
    assertTrue(testInfo.getTags().contains("It is my tag"));
}

See more: JUnit 5 User guide, TestInfo javadoc.

Solution 4 - Java

Try this instead:

public class MyTest {
        @Rule
        public TestName testName = new TestName();
        
        @Rule
        public TestWatcher testWatcher = new TestWatcher() {
            @Override
            protected void starting(final Description description) {
                String methodName = description.getMethodName();
                String className = description.getClassName();
                className = className.substring(className.lastIndexOf('.') + 1);
                System.err.println("Starting JUnit-test: " + className + " " + methodName);
            }
        };
    
        @Test
        public void testA() {
                assertEquals("testA", testName.getMethodName());
        }
    
        @Test
        public void testB() {
                assertEquals("testB", testName.getMethodName());
        }
}

The output looks like this:

Starting JUnit-test: MyTest testA
Starting JUnit-test: MyTest testB

NOTE: This DOES NOT work if your test is a subclass of TestCase! The test runs but the @Rule code just never runs.

Solution 5 - Java

Consider using SLF4J (Simple Logging Facade for Java) provides some neat improvements using parameterized messages. Combining SLF4J with JUnit 4 rule implementations can provide more efficient test class logging techniques.

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
import org.junit.rules.TestWatchman;
import org.junit.runners.model.FrameworkMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggingTest {

  @Rule public MethodRule watchman = new TestWatchman() {
    public void starting(FrameworkMethod method) {
      logger.info("{} being run...", method.getName());
    }
  };

  final Logger logger =
    LoggerFactory.getLogger(LoggingTest.class);

  @Test
  public void testA() {

  }

  @Test
  public void testB() {

  }
}

Solution 6 - Java

A convoluted way is to create your own Runner by subclassing org.junit.runners.BlockJUnit4ClassRunner.

You can then do something like this:

public class NameAwareRunner extends BlockJUnit4ClassRunner {

    public NameAwareRunner(Class<?> aClass) throws InitializationError {
        super(aClass);
    }

    @Override
    protected Statement methodBlock(FrameworkMethod frameworkMethod) {
        System.err.println(frameworkMethod.getName());
        return super.methodBlock(frameworkMethod);
    }
}

Then for each test class, you'll need to add a @RunWith(NameAwareRunner.class) annotation. Alternatively, you could put that annotation on a Test superclass if you don't want to remember it every time. This, of course, limits your selection of runners but that may be acceptable.

Also, it may take a little bit of kung fu to get the current test name out of the Runner and into your framework, but this at least gets you the name.

Solution 7 - Java

JUnit 4 does not have any out-of-the-box mechanism for a test case to get it’s own name (including during setup and teardown).

Solution 8 - Java

String testName = null;
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
for (int i = trace.length - 1; i > 0; --i) {
    StackTraceElement ste = trace[i];
    try {
        Class<?> cls = Class.forName(ste.getClassName());
        Method method = cls.getDeclaredMethod(ste.getMethodName());
        Test annotation = method.getAnnotation(Test.class);
        if (annotation != null) {
            testName = ste.getClassName() + "." + ste.getMethodName();
            break;
        }
    } catch (ClassNotFoundException e) {
    } catch (NoSuchMethodException e) {
    } catch (SecurityException e) {
    }
}

Solution 9 - Java

Based on the previous comment and further considering I created an extension of TestWather which you can use in your JUnit test methods with this:

public class ImportUtilsTest {
    private static final Logger LOGGER = Logger.getLogger(ImportUtilsTest.class);

    @Rule
    public TestWatcher testWatcher = new JUnitHelper(LOGGER);

    @Test
    public test1(){
    ...
    }
}

The test helper class is the next:

public class JUnitHelper extends TestWatcher {
private Logger LOGGER;

public JUnitHelper(Logger LOGGER) {
	this.LOGGER = LOGGER;
}

@Override
protected void starting(final Description description) {
	LOGGER.info("STARTED " + description.getMethodName());
}

@Override
protected void succeeded(Description description) {
	LOGGER.info("SUCCESSFUL " + description.getMethodName());
}

@Override
protected void failed(Throwable e, Description description) {
	LOGGER.error("FAILURE " + description.getMethodName());
}
}

Enjoy!

Solution 10 - Java

In JUnit 5 TestInfo acts as a drop-in replacement for the TestName rule from JUnit 4.

From the documentation :

> TestInfo is used to inject information about the current test or > container into to @Test, @RepeatedTest, @ParameterizedTest, > @TestFactory, @BeforeEach, @AfterEach, @BeforeAll, and @AfterAll > methods.

To retrieve the method name of the current executed test, you have two options : String TestInfo.getDisplayName() and Method TestInfo.getTestMethod().

To retrieve only the name of the current test method TestInfo.getDisplayName() may not be enough as the test method default display name is methodName(TypeArg1, TypeArg2, ... TypeArg3).
Duplicating method names in @DisplayName("..") is not necessary a good idea.

As alternative you could use TestInfo.getTestMethod() that returns a Optional<Method> object.
If the retrieval method is used inside a test method, you don't even need to test the Optional wrapped value.

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.Test;

@Test
void doThat(TestInfo testInfo) throws Exception {
	Assertions.assertEquals("doThat(TestInfo)",testInfo.getDisplayName());
	Assertions.assertEquals("doThat",testInfo.getTestMethod().get().getName());
}

Solution 11 - Java

JUnit 5 via ExtensionContext

Advantage:

You get to have the added functionalities of ExtensionContext by overriding afterEach(ExtensionContext context).

public abstract class BaseTest {

	protected WebDriver driver;

	@RegisterExtension
	AfterEachExtension afterEachExtension = new AfterEachExtension();

	@BeforeEach
	public void beforeEach() {
		// Initialise driver
	}

	@AfterEach
	public void afterEach() {
		afterEachExtension.setDriver(driver);
	}

}
public class AfterEachExtension implements AfterEachCallback {

	private WebDriver driver;

	public void setDriver(WebDriver driver) {
		this.driver = driver;
	}

	@Override
	public void afterEach(ExtensionContext context) {
		String testMethodName = context.getTestMethod().orElseThrow().getName();
		// Attach test steps, attach scsreenshots on failure only, etc.
		driver.quit();
	}

}

Solution 12 - Java

@ClassRule
public static TestRule watchman = new TestWatcher() {
    @Override
    protected void starting( final Description description ) {
        String mN = description.getMethodName();
        if ( mN == null ) {
            mN = "setUpBeforeClass..";
        }

        final String s = StringTools.toString( "starting..JUnit-Test: %s.%s", description.getClassName(), mN );
        System.err.println( s );
    }
};

Solution 13 - Java

I'd suggest you decouple the test method name from your test data set. I would model a DataLoaderFactory class which loads/caches the sets of test data from your resources, and then in your test case cam call some interface method which returns a set of test data for the test case. Having the test data tied to the test method name assumes the test data can only be used once, where in most case i'd suggest that the same test data in uses in multiple tests to verify various aspects of your business logic.

Solution 14 - Java

You can achieve this using Slf4j and TestWatcher

private static Logger _log = LoggerFactory.getLogger(SampleTest.class.getName());

@Rule
public TestWatcher watchman = new TestWatcher() {
    @Override
    public void starting(final Description method) {
        _log.info("being run..." + method.getMethodName());
    }
};

Solution 15 - Java

I have a Junit4 test class that extends TestCase so the example with @Rule didn't work (as mentioned in other answers).

However, if your class extends TestCase you can use getName() to get the current test name so this works:

@Before
public void setUp() {
  System.out.println("Start test: " + getName());
}

@After
public void tearDown() {
  System.out.println("Finish test: " + getName());
}

Solution 16 - Java

I usually use something like this:

/** Returns text with test method name
	@param offset index of method on call stack to print, 1 for a caller of this method.
	*/
	static String getName(int offset)
	{ 
		Throwable t = new Throwable();
		t.fillInStackTrace();
		return 
               t.getStackTrace()[offset].getMethodName()+":"+t.getStackTrace()[offset].getLineNumber(); 
	};

This is exactly what Exception do use when printing stack trace. Depending on the exact context You may have to figure out correct offset value. It is crude and primitive tough and is not using any fancy modern futures.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDave RayView Question on Stackoverflow
Solution 1 - JavaFroMageView Answer on Stackoverflow
Solution 2 - JavaDuncan JonesView Answer on Stackoverflow
Solution 3 - JavaAndrii AbramovView Answer on Stackoverflow
Solution 4 - JavaYavin5View Answer on Stackoverflow
Solution 5 - JavajourneymanView Answer on Stackoverflow
Solution 6 - Javachris.f.jonesView Answer on Stackoverflow
Solution 7 - Javacordellcp3View Answer on Stackoverflow
Solution 8 - JavajnorrisView Answer on Stackoverflow
Solution 9 - JavaCsaba TenkesView Answer on Stackoverflow
Solution 10 - JavadavidxxxView Answer on Stackoverflow
Solution 11 - JavasilverView Answer on Stackoverflow
Solution 12 - JavaleojkelavView Answer on Stackoverflow
Solution 13 - JavaemeraldjavaView Answer on Stackoverflow
Solution 14 - JavaCoderView Answer on Stackoverflow
Solution 15 - JavaJames CrawfordView Answer on Stackoverflow
Solution 16 - JavaTomaszView Answer on Stackoverflow