JUnit+4.x

=JUnit 4.1= toc If you've used JUnit prior to version 4.0 (e.g. you've been using JUnit in Eclipse version 2.0 to 3.1.2) then here is the first place you want to go to get up to speed: [|JUnit 4.0 in 10 Minutes]. If you are not somewhat familiar with JUnit 4, you can probably follow this material. However, after code examples that use JUnit 4 specific features, you'll notice a Click Here link that will give you more detailed information.

Now that I've used it a bit in Eclipse 3.1.2, I've got an initial recommendation and some examples:
 * Using the assert* methods
 * Using a TimeBomb as a test place holder.
 * TimeBomb Generic Code Explained



Using the assert* methods
In JUnit 3.8.1, test classes inherited from TestCase. Among other things, doing so gave the code access to several assert methods like assertEquals. JUnit 4.x no longer makes this requirement. Of course every solution introduces problems. In this case, I no longer have easy access to assertEquals and other such methods.

Java 5 to the rescue
The recommendation from [|JUnit 4.0 in 10 Minutes] is to use [|static imports]from the org.junit.Assert class to get methods like assertEquals. The following excerpt is taken from Example 1 below. Note the line numbers are from the original example. Line 3 is the static import, which is used on lines 29 and 30: code 03: import static org.junit.Assert.assertEquals; 14: public class TestVehicle { 26:    @Test 27:    public void createSimpleVehicle { 28:        Vehicle v = new Vehicle("Ford", "Focus", 2005, "Green", type, license); 29:        assertEquals(type, v.getType); 30:        assertEquals(license, v.getLicense); 31:    }     41: } code  Click Here for more information on @Test.

This works fine until you try to organize imports in Eclipse or use name completion (ctrl-space). Eclipse will not allow you to use name completion on something like assertFalse. You have to manually type in the name, and then manually add the import and things work fine. I'm lazy and I don't want to do this. My first attempt to fix this was the following: code 03: import static org.junit.Assert.*; code Now I can use name completion on things like "assert" and Eclipse will give me my list of names. This works great until you organize imports. As soon as you do the line that contained the .* is replaced by one to many lines, one each for each of the assert* methods you've used.

Since these methods are in the class org.junit.Assert, I've decided to switch to having my test classes inherit from Assert. This works just fine. It sort of defeats the purpose of using annotations to avoid having to use inheritance but it works well with my development environment so I'm happy. Here's an example taken from Example 2. Notice that by extending on line 17, I have easy access to assertEquals on lines 39 and 40. I understand that this violates the is-a interpretation of inheritance. It is not my preference but until we get better IDE support, it makes writing my tests a bit easier. Anything that supports writing tests is a good thing as far as I'm concerned. code 05: import org.junit.Assert; 17: public class VehicleTypeComponentTest extends Assert { 33: 34:    @Test 35:    public void createVehicleTypeWhenNoneExist { 36:        component.clearAll; 37:        component.createVehicleType("Test1", ValidState.valid); 38:        VehicleType vt = component.getVehicleTypeNamed("Test1"); 39:        assertEquals(vt.getName.getValue, "Test1"); 40:        assertEquals(vt.getState.getValue, ValidState.valid); 41:    } 101: } code 

TimeBomb
What is a TimeBomb? Let's begin with an example. This is an excerpt from Example 2: code 05: import org.junit.Assert; 15: import vehicle.util.TimeBomb; 16: 17: public class VehicleTypeComponentTest extends Assert { 92:    @Test(expected = ObjectInUse.class) 93:    public void removeUsedValidType { 94:        TimeBomb.throwUntil(16, 6, 2006, new ObjectInUse(VehicleType.class, "key")); 95:    } 96: code  Click here for more information on @Test(expected = ObjectInUse.class)

This example probably needs a little more background. As mentioned in [|JUnit 4.0 in 10 Minutes], we use the @Test annotation to denote a method as a test case. It can take an optional argument of //**expected**//. This test is meant to attempt to remove a VehicleType that is used by other ob jets. So, in this case, read @Test(expected = ObjectInUse.class) as "when this test executes, I expect the exception ObjectInUse to be thrown."

If I had a Dao (data access object) that used an underlying database, the request would generate a low-level SQL exception related to a constraint violation. Right now I'm just mocking everything out so I don't have that underlying support. I'll add it to the mock but until I do, I want a place holder for this test.

I do not have a try block and I did not add a throws clause to the method signature, so how does this example compile? ObjectInUse is a RuntimeException. The ``@Test(expected = ...)`` does not obviate the need to handle exceptions properly. If I was expecting a checked exception, then along with @Test(expected=SomeCheckedException.class), I'd have to add "throws SomeCheckedException" to the method signature. I could catch the exception in the test case, but I don't recommend that. Let JUnit report exceptions properly. That's one of the things it does for you.

What happens when you do not have the infrastructure in place for a unit test, what are your options?
 * 1) Write the test, run the tests with that test failing until the infrastructure is in place
 * 2) Wait to add the test to the suite
 * 3) Use a TODO comment or some such IDE feature
 * 4) Add the test and get the infrastructure in place right now
 * 5) Use a TimeBomb

The first option is fine if I'm the only person working on the system. However if I'm working in a team then it really isn't. If you happen to be using continuous integration, this option is even less appealing since the build will remain broken until I can fix this test.
 * Option 1**

The second option is good but I'm worried I might forget to add the test in. The way my brain works, once I've generally finished a suite of tests in an area I only go back when I start having failures of some kind or if I think of a test I missed. I like having a place holder. Sure, I can look over all of the user stories and the acceptance tests to make sure I didn't miss anything, but I'd rather just stub out all of the tests I'm going to need to write, write a few, get them to compile and run, then write a few more. Your experience may be different.
 * Option 2**

The third option is a good one. The IDE can remind me to do something. It's somewhat passive since it doesn't force me to do anything. In my experience as a consultant, large projects end up with numerous warnings and todo's. They end up being noise rather than useful information. I don't like this trend but it's what I've experienced. So I'm not keen on this option alone. I think, however, it might be used in conjunction with other options.
 * Option3**

If you can use the forth option, that's the way to go. For this example, which I have taken from some work I'm doing right now, I don't have this option yet. I'm not ready to get to that building block. It will happen in a few days to a week depending on my free time. So for me, this option is not applicable for this situation. If it were, I'd use it.
 * Option 4**

This leaves the TimeBomb example. This test expects an exception to be thrown. I'm using the TimeBomb class to throw the necessary exception until some time in the future. If I have not remembered to go back and write this test by that future date, TimeBomb will stop throwing the exception and the test will start to fail. It allows me to put a place holder in with an //**active**// reminder to fix it at some point in the future.
 * Option 5**

I've used this on what has grown to a team of around 60 people (from 6) all working on different applications based on a common architecture. We've been using this kind of thing now for over 3 years and it seems to remain a valuable technique. You can review the code for TimeBomb below. Since I've written it from scratch on this example, it's pretty small. As I need more methods, I'll add them. It's the idea that is valuable, not the implementation.

If you're interested in a complex and detailed explanation of the implementation of TimeBomb, click here for a detailed description.

Complete Examples
This section contains the full code for the examples mentioned above.



Example 1
code format="java5" 01: package ztest.vehicle.domain; 02: 03: import static org.junit.Assert.assertEquals; 04: import junit.framework.JUnit4TestAdapter; 05: 06: import org.junit.Before; 07: import org.junit.Test; 08: 09: import vehicle.domain.IssuingState; 10: import vehicle.domain.Vehicle; 11: import vehicle.domain.VehicleLicense; 12: import vehicle.domain.VehicleType; 13: 14: public class TestVehicle { 15:    private IssuingState state; 16:    private VehicleLicense license; 17:    private VehicleType type; code  For a description of @Before, click here. code format="java5" 19:    @Before 20:    public void setup { 21:        type = new VehicleType("Luxury"); 22:        state = new IssuingState("Iowa"); 23:        license = new VehicleLicense("LRX24J", state); 24:    } code For a description of @Test, click here. code format="java5" 26:    @Test 27:    public void createSimpleVehicle { 28:        Vehicle v = new Vehicle("Ford", "Focus", 2005, "Green", type, license); 29:        assertEquals(type, v.getType); 30:        assertEquals(license, v.getLicense); 31:    } code For a description of the suite method, click here. code format="java5" 33:    /** 34:      * Provide backwards-compatibility with JUnit runner in Eclipse. 35:     * 36:      * @return 37:     */ 38:     public static junit.framework.Test suite { 39:        return new JUnit4TestAdapter(TestVehicle.class); 40:    } 41: } code
 * TestVehicle.java**



Example 2
code format="java5" 01: package ztest.vehicle.component.vehicletype; 02: 03: import junit.framework.JUnit4TestAdapter; 04: 05: import org.junit.Assert; 06: import org.junit.Before; 07: import org.junit.Test; 08: 09: import vehicle.component.vehicletype.VehicleTypeComponent; 10: import vehicle.domain.VehicleType; 11: import vehicle.domain.ValidState; 12: import vehicle.exception.ObjectDoesNotExist; 13: import vehicle.exception.ObjectExists; 14: import vehicle.exception.ObjectInUse; 15: import vehicle.util.TimeBomb; 16: 17: public class VehicleTypeComponentTest extends Assert { 18: 19:    private VehicleTypeComponent component; 20: 21:    public static junit.framework.Test suite { 22:        return new JUnit4TestAdapter(VehicleTypeComponentTest.class); 23:    } 24: 25:     @Before 26:    public void setup { 27:        component = new VehicleTypeComponent; 28:        component.createVehicleType("PreExisting1", ValidState.valid); 29:        component.createVehicleType("PreExisting2", ValidState.valid); 30:        component.createVehicleType("PreExisting3", ValidState.invalid); 31:        component.createVehicleType("PreExisting4", ValidState.valid); 32:    } 33: 34:     @Test 35:    public void createVehicleTypeWhenNoneExist { 36:        component.clearAll; 37:        component.createVehicleType("Test1", ValidState.valid); 38:        VehicleType vt = component.getVehicleTypeNamed("Test1"); 39:        assertEquals(vt.getName.getValue, "Test1"); 40:        assertEquals(vt.getState.getValue, ValidState.valid); 41:    } 42: 43:     @Test 44:    public void createVehicleTypeWhenSomeExist { 45:        component.createVehicleType("Test1", ValidState.valid); 46:        VehicleType vt = component.getVehicleTypeNamed("Test1"); 47:        assertEquals(vt.getName.getValue, "Test1"); 48:        assertEquals(vt.getState.getValue, ValidState.valid); 49:    } 50: 51:     @Test(expected = ObjectExists.class) 52:    public void createVehicleTypeWhenNameAlreadyExists { 53:        component.createVehicleType("PreExisting1", ValidState.valid); 54: 55:    } 56: 57:     @Test(expected = ObjectExists.class) 58:    public void createVehicleTypeWhenInvalidOneExists { 59:        component.createVehicleType("PreExisting3", ValidState.invalid); 60:    } 61: 62:     @Test 63:    public void invalidteValidVehicleType { 64:        VehicleType vt = component.getVehicleTypeNamed("PreExisting1"); 65:        assertEquals(vt.getState.getValue, ValidState.valid); 66:        component.setVehicleState("PreExisting1", ValidState.invalid); 67:        vt = component.getVehicleTypeNamed("PreExisting1"); 68:        assertEquals(vt.getState.getValue, ValidState.invalid); 69:    } 70: 71:     @Test 72:    public void invalidteInvalidVehicleType { 73:        VehicleType vt = component.getVehicleTypeNamed("PreExisting3"); 74:        assertEquals(vt.getState.getValue, ValidState.invalid); 75:        component.setVehicleState("PreExisting1", ValidState.invalid); 76:        vt = component.getVehicleTypeNamed("PreExisting1"); 77:        assertEquals(vt.getState.getValue, ValidState.invalid); 78:    } 79: 80:     @Test(expected = ObjectDoesNotExist.class) 81:    public void removeUnusedValidVehicleType { 82:        component.removeVehicleTypeNamed("PreExisting1"); 83:        component.getVehicleTypeNamed("PreExisting1"); 84:    } 85: 86:     @Test(expected = ObjectDoesNotExist.class) 87:    public void removeUnusedInvalidVehicleType { 88:        component.removeVehicleTypeNamed("PreExisting3"); 89:        component.getVehicleTypeNamed("PreExisting3"); 90:    } 91: 92:     @Test(expected = ObjectInUse.class) 93:    public void removeUsedValidType { 94:        TimeBomb.throwUntil(16, 6, 2006, new ObjectInUse(VehicleType.class, "key")); 95:    } 96: 97:     @Test(expected = ObjectInUse.class) 98:    public void removeUsedInvalidType { 99:        TimeBomb.throwUntil(16, 6, 2006, new ObjectInUse(VehicleType.class, "key")); 100:    } 101: } code
 * VehicleTypeComponentTest.java**



Example 3
code format="java5" 01: package ztest.vehicle.component.rateplan; 02: 03: import junit.framework.JUnit4TestAdapter; 04: 05: import org.junit.After; 06: import org.junit.AfterClass; 07: import org.junit.Assert; 08: import org.junit.Before; 09: import org.junit.BeforeClass; 10: import org.junit.Test; 11: 26: public class RatePlanComponentTest extends Assert { 27:    private static final String TEST_VEHICLE_TYPE_NAME = "TestVehicleType"; 28:    private static final String TEST_VEHICLE_TYPE_NAME_2 = "TestVehicleType2"; 29: 30:    private RatePlanComponent component; 31:    private static VehicleTypeComponent vtComponent; 32: 33:    private Field createdRatePlanName; 34:    private Field createdRatePlanVehicleType; 35: 36:    public static junit.framework.Test suite { 37:        return new JUnit4TestAdapter(RatePlanComponentTest.class); 38:    } code  For a description of @BeforeClass, click here. code format="java5" 40:    @BeforeClass 41:    public static void createTestVehicleType { 42:        vtComponent = new VehicleTypeComponent; 43:        vtComponent.createVehicleType(TEST_VEHICLE_TYPE_NAME, ValidState.valid); 44:        vtComponent.createVehicleType(TEST_VEHICLE_TYPE_NAME_2, ValidState.valid); 45:    } code  For a description of @AfterClass, click here. code format="java5" 47:    @AfterClass 48:    public static void removeTestVehicleType { 49:        vtComponent.removeVehicleTypeNamed(TEST_VEHICLE_TYPE_NAME); 50:        vtComponent.removeVehicleTypeNamed(TEST_VEHICLE_TYPE_NAME_2); 51:    } 52: 53:     @Before 54:    public void setup { 55:        component = new RatePlanComponent; 56:        disableDeleting; 57:    } code  For a description of @After, click here. code format="java5" 59:    @After 60:    public void removeCreatedRateplan { 61:        if (createdRatePlanName != null && createdRatePlanVehicleType != null) { 62:            component.removeRatePlan(createdRatePlanName, createdRatePlanVehicleType); 63:        } 64: 65:     } 66: 67:     @Test 68:    public void createRatePlan { 69:        RatePlan rp = instantiatePopulatedRatePlan("Test Plan", TEST_VEHICLE_TYPE_NAME, 29, 137.75f, 14, 10, 34); 70:        Result result = component.createRatePlan(rp); 71:        assertTrue(result.isSuccess); 72:    } 73: 74:     @Test 75:    public void createRatePlanSameNameDifferentVehicleType { 76:        RatePlan rp1 = instantiatePopulatedRatePlan("Test Plan", TEST_VEHICLE_TYPE_NAME, 34, 138, 12, 10, 40); 77:        RatePlan rp2 = instantiatePopulatedRatePlan("Test Plan", TEST_VEHICLE_TYPE_NAME_2, 34, 138, 12, 10, 40); 78: 79:        try { 80:            Result result1 = component.createRatePlan(rp1); 81:            assertTrue(result1.isSuccess); 82: 83:            Result result2 = component.createRatePlan(rp2); 84:            assertTrue(result2.isSuccess); 85:        } finally { 86:            removeRatePlan(rp1); 87:            removeRatePlan(rp2); 88:        } 89:     } 90: 91:     @Test 92:    public void createRatePlanSameVehicleTypeDifferentName { 93:        RatePlan rp1 = instantiatePopulatedRatePlan("Test Plan1", TEST_VEHICLE_TYPE_NAME, 34, 138, 12, 10, 40); 94:        RatePlan rp2 = instantiatePopulatedRatePlan("Test Plan2", TEST_VEHICLE_TYPE_NAME, 34, 138, 12, 10, 40); 95: 96:        try { 97:            Result result1 = component.createRatePlan(rp1); 98:            assertTrue(result1.isSuccess); 99: 100:            Result result2 = component.createRatePlan(rp2); 101:            assertTrue(result2.isSuccess); 102:        } finally { 103:            removeRatePlan(rp1); 104:            removeRatePlan(rp2); 105:        } 106:     } 107: 108:     @Test(expected = ObjectExists.class) 109:    public void createRatePlanAlreadyExists { 110:        RatePlan rp = instantiatePopulatedRatePlan("Test Plan1", TEST_VEHICLE_TYPE_NAME, 34, 138, 12, 10, 40); 111:        component.createRatePlan(rp); 112:        component.createRatePlan(rp); 113:    } code  What is this doing here? Click here. code format="java5" 233:    private RatePlan instantiateBasicRatePlan(String name, String vehicleTypeName) { 234:        createdRatePlanName = new Field(name); 235:        createdRatePlanVehicleType = new Field(vtComponent.getVehicleTypeNamed(vehicleTypeName)); 236: 237:        return new RatePlan(createdRatePlanName, 0, 0, 0, 0, 0, createdRatePlanVehicleType, ValidState.valid); 238:    } code
 * RatePlanComponentTest.java** (partial)



TimeBomb.java
code format="java5" 01: package vehicle.util; 02: 03: import java.util.Calendar; 04: 05: public class TimeBomb { 06:    public static void throwUntil(int d, int m, int y, T e) throws T { 07:        Calendar now = Calendar.getInstance; 08:        Calendar dateToThrowUntil = Calendar.getInstance; 09:        dateToThrowUntil.set(y, m - 1, d); 10:        11:         if(dateToThrowUntil.after(now)) { 12:            throw e; 13:         } 14:    } 15: } code Click here for a detailed description of TimeBomb.