JUnit, a simple testing framework created by Kent Beck and Erich Gamma during a long airplane trip a few years ago, ushered in a new era of developer testing. A cottage industry grew up around "*Unit" clones, for example NUnit, a version used to unit test Microsoft .Net programs.
Kent Beck (left) and Alberto Savoia
JUnit 3.8 was stable and simple, and a couple of years went by with no updates. That's not so bad, according to Kent, who spoke last week at JavaOne. There's a case to be made for getting something to a useful state -- you don't have to worry about keeping up with APIs and changing semantics. But eventually,
"A number of things convinced us to start it up again. One was the appearance of other testing frameworks [like TestNG], and the other was the new features in Java 5."Kent and Erich asked themselves, how could they make a framework that's even simpler than JUnit 3.8; one that's more approachable, has a smaller learning curve, and a simpler API? JUnit 4 is the result. According to Erich, development of JUnit 4 was not carried out on a plane this time, but was done mostly using instant messaging and VNC, a free remote desktop product.
JUnit in general, and especially the new version, was designed with a few simple goals in mind:
- Approachability. There are a bunch of design decisions that would have gone differently if it JUnit had been targeted for professional testers. For example the choice of Java as the "testing language".
- The tests need to be completely isolated from each other. The results of one tests shouldn't affect the results of another test. You should be able to swap the tests and not change the outcome.
- The tools need to be fast. The key is to get in a tight cycle where there's a little test, a little code, back and forth. If there's a lot of overhead you can't do that. Often 2 and 3 conflict with each other.
- JUnit has to be flexible. Kent says, "If everybody can modify the framework to exactly suit their needs then I consider that successful."
- You're free to use any superclass for tests: you don't have to extend TestCase.
- Fixture methods are identified by @Before and @After. Now it's possible to have multiple fixture methods.
- Test methods are identified by @Test, so instead of "testSize()" you have "@Test size()". This tells your IDE it's a method that should be run as a test. Also annotations let you simplify tests that have expected exceptions.
- Static imports are used to get the assertion methods, so special purpose assertions are easy to integrate.
- JUnit 4 is forward and backward compatible, so you can run JUnit3.8 tests with JUnit4 testrunners, and JUnit4 tests with JUnit3.8 testrunners.
Kent: "When Erich and I were flying to Atlanta from Zürich, JUnit got started on the airplane. At that time our perceptions of what we had done were, we'd written a little program, it was pretty cool, but it was so small and seemingly insignificant that of course the only thing we could do with it was give it away for free.
"In jazz they have this saying: just because you can doesn't mean you should. The problem is in software design often the consequences of your decisions don't become apparent for years. One of the advantages of having to live with JUnit for 8 years, is now we can look back and see which decisions we made worked nicely and which we would have done differently. And we were able to apply those lessons in JUnit 4. These include:
- Composite is a fabulous design pattern. but has hidden consequences. Test is a tempting target for extensions. When running a test down at the bottom, every parent has to run its setup. If I want to rerun the single test case, it's ugly. The JUnit3 tree was used for 3 different purposes: to decide what tests should be run, to run the tests, and to report the results. In a "cleverness" sort of way it might be cool but it causes headaches. So in JUnit4 we split that into a Request object, Runners, and Description of results. TestCase isn't involved in so many patterns now.
- There's nothing like success. We didn't expect people would extend JUnit. When you offer up powerful forms of flexibility there will be costs, for example it'll be difficult to maintain and still have backwards compatibility.
- Good ideas aren't enough. There were testing tools available, and JUnit is just not that complicated. Many people had the skill to do it. Lots of people could have done JUnit, lots of people could have written tests so why didn't they? The lesson is that the availability of a tool can facilitate change.
- Cultural changes take decades to take hold. It's like science: they say science progresses one funeral at a time. Software development progresses one promotion at a time. The basic ideas [for developer testing] have been around for a long time, and I expect the consequences -- its widespread adoption, and the things it makes possible -- will play out over a long period of time.
Kent: "We could have said there's a new generation of testing tools, we'll let them have the field. But our commitment is to provide the simplest tool we can imagine for developers writing tests, and we accept that responsibility. We thought we were just programming on an airplane.
"Try JUnit 4 and give us your feedback so we can continue to improve on it."
Take-away quote: "I happened to be the one to write it with Erich, so that's why I'm up here and you're sitting down there." - Kent Beck