Friday, February 13, 2009

JUnit 3 & JUnit 4: Oil & water

Version 4 of the popular JUnit test framework has been available for use for quite some time (In fact, it is up to version 4.5). However, many projects have a wealth of JUnit 3-style tests and developers may choose to continue using it. If, on the other hand, you decide to dip your toes in JUnit 4 waters, make it a complete immersion. Don't try to mix and match.

As you may be aware, couple of the touted "benefits" of JUnit 4 are the obviation of extending the TestCase class and the identification of test and lifecycle methods (setUp and tearDown) using Java 5 annotations. For example, the following is a valid JUnit 4-style test case (minus the necessary imports):

public class BusinessLogicTests {
  @Before
  public void do_this_before_every_test() {
    // set up logic goes here
  }

  @After
  public void clean_up_after_a_test() {
    // teardown logic goes here
  }

  @Test
  public void addition() {
    assertEquals("Invalid addition", 2, 1+1);
  }
}

Observe how

  • The class does not extend TestCase

  • The "setup" and "tearDown" methods are identified by annotations

  • The test method does not require a "test" prefix and is also annotated


I'll let you be the judge of whether this is an improvement on JUnit 3. What I want to alert you to are the perils of subconciously mixing JUnit-3 and JUnit-4 style programming. Consider the following test case:

public class BusinessLogicTests extends TestCase {
  @Before
  public void do_this_before_every_test() {
    // set up logic goes here
  }

  @After
  public void clean_up_after_a_test() {
    // teardown logic goes here
  }

  @Test
  public void testAddition() {
    assertEquals("Invalid addition", 2, 1+1);
  }

  @Test
  public void subtraction() {
    assertEquals("Invalid subtraction", 1, 1-1);
  }
}

This will result in a green-bar even though there is an error in the test of subtraction. Can you guess why?

It was that pesky little extends TestCase that I added from force of habit. The flips the switch to JUnit-3, which knows nothing about annotations, ignores my setup and teardown methods and looks for methods prefixed by test. It completely ignores the method named subtraction!

And of course, conversely, the following is just a plain old Java class that won't be acknowledged by either JUnit-3 or JUnit-4.

public class BusinessLogicTests {
  public void testAddition() {
    assertEquals("Invalid addition", 2, 1+1);
  }
}

Final word of advice, if you are using Struts Test Case, you have no choice but to write JUnit-3 style tests since the parent class of the framework (MockStrutsTestCase) extends Junit-3's TestCase.

5 comments:

Sree said...

Good finding! but misleading in many ways.
As the issue (of not running another test case), would be found in many ways.

Compile Time:
- extends 'TestCase' not needed any more. the very first sentence that catches your eye when you read any v.4 documentation.

- assertEquals() & similar others, are imported as static, v.4 & JDK5


Runtime:
- When you run the eg. code for v.3, say in eclipse, you'd see only one test run.

By the way, what are the underscores in the name of the methods,
clean_up_after_a_test?!

cwwojcik said...

Excellent example. This is exactly the problem I am running into now. I am moving to junit4, but would like to keep the old junit3 code. I would like to add new tests using junit4 annotation and still allow both versions of tests to be executed.

Were you able to get both tests to execute in your example? If so, how was it done?

Sri Sankaran said...

@cwwojcik
You *can* run JUnit3 & JUnit4-style unit tests in the same project -- just not in the same class.

For JUnit3-style you must extend JUnit's TestCase class. On the other hand for JUnit4 you simply use the annotations. You can run them both as part of the same build.

SkaRootz said...

same problem, I want to keep extending junit 3 base classes but still be able to write JUnit 4 style test methods and setup and teardowns...

I found that sometimes it works overriding setup and teardown (if there is a base class with fixture fuctionality) and annotating the overrided methods with @Before and @After... but not pretty sure if it would work always...

Michael Gower said...

Thanks for the post! I wish I found this before I started converting to JUnit4. I linked to this post from my blog, Hacker's Valhalla, as I wrote a related post about unit testing. Thank you again.

Tweety thoughts

    follow me on Twitter