Get name of currently executing test in JUnit 4
Asked Answered
C

17

267

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.

Coenurus answered 23/1, 2009 at 15:51 Comment(4)
What does the above code give you in JUnit 4?Conjoin
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.Coenurus
I have a similar problem where I want to <b>set</b> the test name since I'm using the Parametrized runner that only gives me numbered test cases.Summary
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... ?Garber
S
425

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());
    }
}
Sapp answered 15/9, 2009 at 11:59 Comment(5)
Also note that TestName is not available in @before :( See: old.nabble.com/…Pesthouse
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.Golanka
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\])?"));Defensible
@DuncanJones Why the proposed alternative is "more efficient"?Waldron
JUnit 4 annotations @Rule and @ClassRule do not exist in JUnit 5. For an updated alternative see here: #52075372Sherrillsherrington
S
130

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());
   }
};
Stilton answered 21/12, 2012 at 9:57 Comment(3)
Why use a public field?Slavocracy
@RaffiKhatchadourian See #14336058Stilton
In Kotlin you have to use @get:RulePappus
P
30

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.

Pigheaded answered 25/12, 2016 at 13:0 Comment(3)
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.Elissa
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 displayNamePassable
i think it is easier/better to use beforeeach with this testinfo - in this case no need to modify each method @BeforeEach public void BeforeEach(TestInfo testInfo) { logger.info("... before each test, {}", testInfo.getDisplayName()); }Masked
C
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.

Chequerboard answered 19/4, 2013 at 21:17 Comment(3)
God bless you for your NOTE at the very of the example.Methionine
"This DOES NOT work" - case in point - cucumber ignores @Rule annotationsAgustin
you also need extra reference to use @Rule , it is much simpler to add @BeforeEach public void BeforeEach(TestInfo testInfo) { logger.info("... before each test, {}", testInfo.getDisplayName()); }Masked
H
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() {

  }
}
Hampson answered 29/12, 2011 at 10:5 Comment(0)
A
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.

Aciniform answered 16/4, 2009 at 21:45 Comment(2)
Conceptually at least, this idea seems rather straightforward to me. My point being: I wouldn't call it convoluted.Tiffanietiffanle
"on a Test superclass ..." - Please, no more of the horrible inheritance based design patterns. This is so JUnit3!Judaist
G
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) {
    }
}
Galimatias answered 30/12, 2010 at 23:5 Comment(2)
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..Nace
@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.Scansorial
R
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).

Rhythmical answered 23/1, 2009 at 15:57 Comment(2)
Is there an not-out-of-the-box mechanism out there other than inspecting the stack?Coenurus
Not the case given the answers below! maybe assign the correct answer to someone else?Ivaivah
D
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!

Dorking answered 23/6, 2016 at 15:51 Comment(3)
Hi what is that ImportUtilsTest, I get an error, it seems to be a logger class, do I have more information? ThanksBloomy
The named class is just an example of a JUnit test class: the user of JUnitHelper. I will correct the usage example.Dorking
Ah now I feel dumb, it was so obvious. Thanks a lot! ;)Bloomy
A
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());
}
Alvie answered 12/8, 2018 at 16:9 Comment(0)
W
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();
    }

}
Whicker answered 25/3, 2020 at 2:25 Comment(2)
why not simple @BeforeEach public void BeforeEach(TestInfo testInfo) { logger.info("... before each test, {}", testInfo.getDisplayName()); } ?Masked
Because ExtensionContext has other added functionalities useful for test reporting, which you'd normally need after a test, not before it.Whicker
P
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 );
    }
};
Pampas answered 12/10, 2012 at 13:13 Comment(0)
L
1

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());
}
Leatherman answered 23/6, 2021 at 1:11 Comment(0)
B
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.

Bronny answered 2/12, 2021 at 19:9 Comment(0)
H
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());
    }
};
Henson answered 14/4, 2015 at 4:52 Comment(0)
A
-1

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.

Arango answered 24/1, 2009 at 21:3 Comment(0)
U
-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
#############################################################
Uigur answered 9/1, 2023 at 12:14 Comment(1)
That's TestNG, not JUnit 4.Whiffen

© 2022 - 2024 — McMap. All rights reserved.