263

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.

Kalle Richter
  • 8,008
  • 26
  • 77
  • 177
Dave Ray
  • 39,616
  • 7
  • 83
  • 82
  • What does the above code give you in JUnit 4? – Bill the Lizard Jan 23 '09 at 15:56
  • 5
    JUnit 3 tests extend TestCase where getName() is defined. JUnit 4 tests do not extend a base class, so there is no getName() method at all. – Dave Ray Jan 23 '09 at 16:01
  • I have a similar problem where I want to set the test name since I'm using the Parametrized runner that only gives me numbered test cases. – Volker Stolz Feb 16 '09 at 01:21
  • Lovely solution using Test or TestWatcher... just wondering (out loud) whether there should ever be a need for this? You can find whether a test is running slowly by looking at the timing output charts given by Gradle. You should never need to know the order in which tests operate... ? – mike rodent Oct 23 '16 at 19:00

17 Answers17

421

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());
    }
}
Shaulian
  • 411
  • 5
  • 8
FroMage
  • 5,038
  • 2
  • 20
  • 13
  • 4
    Also note that TestName is not available in @before :( See: http://old.nabble.com/@Rule-TestName-not-available-in-@Before-methods...-td25198691.html – jm. Nov 05 '09 at 16:34
  • 42
    Apparently newer versions of JUnit execute `@Rule` before `@Before` - I'm new to JUnit and was depending on `TestName` in my `@Before` without any difficulties. – MightyE Apr 16 '10 at 11:36
  • 3
    If you are using parameterized tests "name.getMethodName()" will return {testA[0], testA[1], etc} thus I use some like : assertTrue(name.getMethodName().matches("testA(\\[\\d\\])?")); – Legna May 28 '14 at 13:39
  • 3
    @DuncanJones Why the proposed alternative is "more efficient"? – Stephan Nov 08 '18 at 17:07
  • JUnit 4 annotations `@Rule` and `@ClassRule` do not exist in JUnit 5. For an updated alternative see here: https://stackoverflow.com/questions/52074872/what-is-the-equivalent-of-testname-rule-in-junit-5 – zr0gravity7 Feb 08 '22 at 05:02
129

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());
   }
};
Community
  • 1
  • 1
Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
28

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.

Andrii Abramov
  • 10,019
  • 9
  • 74
  • 96
  • This also works in methods marked `@BeforeEach` and `@AfterEach`. You can also define those methods in an abstract base class that your other test classes inherit/extend from, so that you only need to put the log statements in one spot instead of repeating that in every test class. – ArtOfWarfare Mar 01 '22 at 20:44
  • 1
    Also works for Classes ```@DisplayName("This is my Class") public class MyClass .... @BeforeAll static void beforeAll(TestInfo testInfo) { System.out.println(testInfo.getDisplayName())} ``` will show the Class `displayName` – Mache Jan 04 '23 at 14:35
10

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.

Yavin5
  • 679
  • 10
  • 11
8

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() {

  }
}
Peter O.
  • 32,158
  • 14
  • 82
  • 96
journeyman
  • 81
  • 1
  • 1
6

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.

chris.f.jones
  • 485
  • 4
  • 10
  • Conceptually at least, this idea seems rather straightforward to me. My point being: I wouldn't call it convoluted. – user98761 Dec 13 '12 at 00:08
  • "on a Test superclass ..." - Please, no more of the horrible inheritance based design patterns. This is so JUnit3! – oberlies Aug 06 '13 at 11:00
5
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) {
    }
}
jnorris
  • 61
  • 1
  • 2
  • 1
    I can argue that he only wanted to show a solution .. do not see why the negative vote.... @downvoter: at least, at least, add useful information.. – Victor May 13 '15 at 16:03
  • 1
    @skaffman We all love to see the full range of alternative solutions. This is the closest one for what I'm looking for: Getting the test name not directly in the testclass but in the class which gets used during the test (for example somewhere in a logger component). There, test-relevant annotations don't work anymore. – Daniel Alder Sep 19 '17 at 08:59
4

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).

cordellcp3
  • 3,557
  • 1
  • 17
  • 14
3

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!

Csaba Tenkes
  • 124
  • 7
2

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());
}
davidxxx
  • 125,838
  • 23
  • 214
  • 215
2

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();
    }

}
k_rollo
  • 5,304
  • 16
  • 63
  • 95
1
@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 );
    }
};
leojkelav
  • 276
  • 1
  • 3
  • 8
1

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.

Tomasz
  • 11
  • 1
0

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());
    }
};
Coder
  • 3,090
  • 8
  • 49
  • 85
0

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.

emeraldjava
  • 10,894
  • 26
  • 97
  • 170
0

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());
}
-1

A more simpler way is to put this logic in setUp() and tearDown() methods.
Refer below code for better clarity,

import java.lang.reflect.Method;

@BeforeMethod
void setUp(Method method) {
  log.info("###############################################");
  log.info("Running Test: {}", method.getName());
}

@AfterMethod
void tearDown(Method method) {
  log.info("Finished Test: {}", method.getName());
    log.info("###############################################");
}


@Test
public void testMethodName() {
  // Method logic implementation...
}

Here is the output of above test execution,

#############################################################
Running Test: testMethodName
// Logs related to method execution...
Finished Test: testMethodName
#############################################################
Sanjit
  • 57
  • 8