What's new in JUnit 4
Due to the use of Java 5 annotations, JUnit 4 is more lightweight and flexible. It has dropped its strict naming conventions and inheritance hierarchies in favor of some new functionality. Here is a short list of what is new in JUnit 4:
Java 5 Annotations
JUnit 4 uses Java 5 annotations to completely eliminate both of these conventions. The class hierarchy is no longer required and methods intended to function as tests need only be decorated with a newly defined @Test annotation:
import static org.junit.Assume.assumeTrue;
import static org.junit.Assert.fail;
import org.junit.BeforeClass;
import org.junit.Test;
public class DemoLevelTests {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
// This method will be run once before any of the test methods in the class.
}
@Test
public void testAssumptions() {
assumeTrue("A condition for executing the test was not fulfilled.", false);
fail("This method will be skipped!");
}
}
Java 5's static import feature is used to import the Assume class's assumeTrue() method. This is because test classes do not extend from TestCase as they did in previous versions of JUnit.
In JUnit 4, suite semantics have been replaced with two new annotations. The first one, @RunWith, is designed to facilitate having different runners (other than the ones built into the framework) execute a particular test class. JUnit 4 bundles a suite runner, named Suite, that you must specify in the @RunWith annotation. What is more, you must provide another annotation, named @SuiteClasses, which takes as a parameter a list of classes intended to represent the test suite.
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({ParametricRegularExpressionTest.class,
RegularExpressionTest.class,
TimedRegularExpressionTest.class})
public class JUnit4Suite {
}
Testing With Timeouts
In JUnit 4, a test case can take a timeout value as a parameter. As you can see below, the timeout value represents the maximum amount of time the test can take to run: if the time is exceeded, the test fails.
@Test(timeout=1000)
public void testAssumptions() {
assumeTrue("A condition for executing the test was not fulfilled.", false);
fail("This method will be skipped!");
}
The timeout is measured in milliseconds.
Fixtures
Fixtures foster reuse through a contract that ensures that particular logic is run either before or after a test. In older versions of JUnit, this contract was implicit regardless of whether you implemented a fixture or not. JUnit 4, however, has made fixtures explicit through annotations, which means the contract is only enforced if you actually decide to use a fixture. Through a contract that ensures fixtures can be run either before or after a test, you can code reusable logic. This logic, for example, could be initializing a class that you will test in multiple test cases or even logic to populate a database before you run a data-dependent test. Using fixtures ensures a more manageable test case: one that relies on common logic. Fixtures are very useful when running many tests that use the same logic and some or all of them fail. Rather than sifting through each test's set-up logic, you can look in one place to deduce the cause of failure. Older versions of JUnit employed a somewhat inflexible fixture model, where you had to wrap every test method by setUp() and tearDown() methods.
JUnit 4 uses annotations to cut out a lot of the overhead of fixtures, allowing you to run a fixture for every test or just once for an entire class or not at all. There are four fixture annotations: two for class-level fixtures and two for method-level ones. At the class level, you have @BeforeClass and @AfterClass, and at the method (or test) level, you have @Before and @After as shown below:
import static org.junit.Assume.assumeTrue;
import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* A TestCase that demonstrates the different levels in the JUnit 4 TFW.
*
* @version 1.0.0
*/
public class DemoLevelTests {
/**
* @throws java.lang.Exception
*/
@BeforeClass
public static void setUpBeforeClass() throws Exception {
// This method will be run once before any of the test methods in the class.
}
/**
* @throws java.lang.Exception
*/
@AfterClass
public static void tearDownAfterClass() throws Exception {
// This method will be run once after all the tests in the class have been run.
}
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
// This method will be run each time before any test method.
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
// This method will be run each time after any test method.
}
@Test
public void testAssumptions() {
assumeTrue("A condition for executing the test was not fulfilled.", false);
fail("This method will be skipped!");
}
}