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.
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());
}
}
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());
}
};
public
field?
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.
@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.
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.
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() {
}
}
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.
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).
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) {
}
}
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!
ImportUtilsTest
, I get an error, it seems to be a logger class, do I have more information? Thanks
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());
}
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();
}
}
@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 );
}
};
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.
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());
}
};
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());
}
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.
Success story sharing
@Rule
before@Before
- I'm new to JUnit and was depending onTestName
in my@Before
without any difficulties.