Friday, December 11, 2009

How to determine the JUnit 4 current test name

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


In my unit tests I often want to know the name of the test that is executing. For example, I often want to have the golden file (expected test output) computed automatically from the testname. As another example in my JavaFX testing, I often generate screenshots inside failing tests and it's useful to name these screenshots by the failing tests.



In JUnit 3 this was simple, since your testcases would extend a builtin JUnit class which had a method you could call to return the current test. However, with JUnit 4 that's no longer possible. I've googled and found the "correct" way to do it - using a special @RunWith to run the test class - but I find that solution unsatisfying. My utility methods which are invoked to read golden files and screenshot etc are in one place and I now have to decorate all my tests. Besides, I already have a @RunWith annotation on my tests because I want to run them on the event dispatch thread so I have a special test runner for that.



So, I've found a better way to do it. Better for me I mean - this may have problems and limitations I'm not aware of, but for all of my tests this worked wonderfully, and doesn't have the @RunWith requirements (though note that I don't do multithreading in my tests, other than invoke them on the event dispatch thread, so if you try to call this from a thread that didn't invoke the test it probably won't work):


public static String getTestName() {
// Try to find a method on the stack which is annotated with @Test -- if so, that's the one
StackTraceElement[] elements = new Throwable().fillInStackTrace().getStackTrace();
for (int i = 1; i < elements.length; i++) {
StackTraceElement element = elements[i];
try {
Class clz = Class.forName(element.getClassName());
Method method = clz.getMethod(element.getMethodName(), new Class[0]);
for (Annotation annotation : method.getAnnotations()) {
if (annotation.annotationType() == org.junit.Test.class) {
return element.getMethodName();
}
}
} catch (NoSuchMethodException ex) {
} catch (SecurityException ex) {
} catch (ClassNotFoundException classNotFoundException) {
}
}

// Just assuming it's the calling method
return elements[1].getMethodName();
}

As with most of my test utilities, it's a public static method living in a class called TestUtils, which I statically import from my test cases such that I can simply reference the test name getter like I would in the JUnit 3 days:

import static org.junit.Assert.*;
import static my.package.name.TestUtils.*;

/* ... */

screenshot(scene, getTestName());

By the way if you're using JavaFX you might be interested in the screenshot utility method. It's really simple:

public static File screenshot(Scene scene, String fileName) throws Exception {
BufferedImage image = (BufferedImage) scene.renderToImage(null);
if (!fileName.endsWith(".png")) {
fileName = fileName + ".png";
}
File file = new File(getScreenshotDir(), fileName);
file.createNewFile();
ImageIO.write(image, "png", file);
return file;
}

(where obviously getScreenshotDir() returns a File folder where you want your screenshots generated. A decent default implementation is return new File(System.getProperty("java.io.tmpdir")); ...)