20
Mar
Quick step-by-step guide : Upgrading Junit 3.x to Junit 4.x
Posted By Ian Homer Friday, March 20, 2009. 2 Comments
Well we finally got around to upgrade all our unit tests to new features. About time really!
We use maven and the maven-surefire-plugin for running our unit tests during our build process. Let’s look at the steps I did to get migrate.
- All our test cases extend from a single abstract test case class called AbstractLiveTestCase that in our Junit 3 integration extended from junit.framework.TestCase. For Junit 4, tests are described with annotations so we don’t need to extend this class anymore – so I (1) removed the “extends TestCase” from our AbstractLiveTestCase
- The Junit 3 TestClass class used to provide all the handy assert functions. These are now provided as org.junit.Assert static methods so I went through each of our tests and (2) added “import static org.junit.Assert.*;” to the top of each class. With the handing auto source code formatting in Eclipse enabled, Eclipse automatically expanded the “org.junit.Assert.*” imports to the explicit ones needed for that class when I saved it.
- And finally (3) Added @Test to each test method so that Junit would know what tests to run.
Basically that was it – surefire/maven/junit picked up the test cases in the new style – and in most cases that’s job done. However we wanted to go a little further.
We had also implemented in Junit 3 a handy feature that outputted a quick ERROR message in the system out of a test run so we could quickly see the failure. By default maven’s surefire simply tells you there was a failure, but you have to look at a file to see what it was. Bit of pain that, so in our Junit 3 extension we would provide more info on the screen when a failure occurred, for example we might see something like:
Running com.bemoko.live.platform.mwc.sites.LiveSiteTestCase
ERROR : *** Test Failure *** Site home not correct expected: but was: @ line 17 of com.bemoko.live.platform.mwc.sites.LiveSiteTestCase (com.bemoko.live.platform.BemokoTestListener)
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.01 sec <<< FAILURE!
In Junit 3 we did this by overriding the runTest method of the TestCase class. In Junit 4 we can use runners and listeners. To start with we set the @RunWith annotation on our AbstractLiveTestCase to indicate that all of our tests should run with a specified runner …
@RunWith(BemokoTestRunner.class) public abstract class AbstractLiveTestCase {
The runner was defined to register a listener …
1 2 3 4 5 6 7 8 9 10 | public class BemokoTestRunner extends BlockJUnit4ClassRunner { public BemokoTestRunner(Class<?> clazz) throws InitializationError { super(clazz); } @Override public void run(final RunNotifier notifier) { notifier.addListener(new BemokoTestListener()); super.run(notifier); } } |
and the listener was defined to listen for failures …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public class BemokoTestListener extends RunListener { private static Log log = LogFactory.getLog(BemokoTestListener.class); @Override public void testFailure(Failure failure) { Throwable t = failure.getException(); if (t instanceof Exception) { log.error("*** Test Failure *** " + t.getMessage(), t); } else { if (t.getMessage() != null) { log.error("*** Test Failure *** " + t.getMessage() + " @ " + getErrorLocation(t.getStackTrace())); } else { log.error("*** Test Failure *** ", t); } } } @Override public void testIgnored(Description description) { if (log.isWarnEnabled()) { log.warn("+++ Test ignored +++ " + description.getDisplayName()); } } private String getErrorLocation(StackTraceElement[] st) { for (StackTraceElement ste : st) { if (ste.getClassName().startsWith("com.bemoko") && !ste.getClassName().contains("AbstractLiveTestCase")) { return "line " + ste.getLineNumber() + " of " + ste.getClassName(); } } return "(line number and class not known)"; } } |
in this way I can handle events from my tests in any way I see fit and much more elegant that the Junit 3 implementation I previously had.
Job done and we’re all sorted for our unit test framework for the foreseeable future.
Posted in: java

Comments
Craig Gallen
Nov 7th, 2009
Hi,
Really useful little technique for logging test results. Thanks
Tom Castle
Jan 5th, 2011
Thanks! This was useful for automating my upgrade to JUnit 4. A couple of extra upgrades most will require is annotating any setUp methods with @Before, and tearDown methods with @After. Both of which previously overrided methods in TestCase.
Leave a Comment