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:
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 importsfrom 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:
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:
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.
TimeBomb
What is a TimeBomb? Let’s begin with an example. This is an excerpt from Example 2:
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?
Write the test, run the tests with that test failing until the infrastructure is in place
Wait to add the test to the suite
Use a TODO comment or some such IDE feature
Add the test and get the infrastructure in place right now
Use a TimeBomb
Option 1
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 2
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.
Option3
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.
Option 4
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 5
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.
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.
Comments