April 2004 archives from
Piotr's R&D blog

Nested test classes

Thursday, April 08, 2004, 04:03PM - category Java -

Bruce Eckel has posted an idea about embedding JUnit tests into comments, to keep them closer to the code being tested. The resulting TSS discussion trashed the idea pretty thoroughly, and deservedly so. But as somebody else mentioned, the idea is right, it's just the implementation that's weak, so I thought I'd take this occasion to present the structure I've been using for writing JUnit tests in my recent project.

The basic idea is to nest your JUnit test classes inside the classes they're testing. Naturally, as with everything, there are tradeoffs. Advantages:

  1. Tests stay close to the code being tested, making them more likely to be udpated and serving as a kind of imperative documentation.
  2. Tests have access to private members of the class being tested. This is important for unit tests, since they're supposed to be white-box, and I don't want to pollute my class's interface with (even package-visibility) accessors. Yes, I know accessing a private member will generate a package-level accessor behind the scenes. However, the compiler still respects the original protection of the member, and anybody using VM-level visibility protection for access security must be crazy.
  3. Contrary to Bruce's scheme, you retain the full benefits of your IDE, including completion, outlining, etc.
Note that IDEs (or at least Eclipse) do pick up on nested test classes, so you don't need to do anything special to run your test suite.

There are disadvantages as well, though they can be mitigated:

  1. It's more difficult to exclude the test code from a distribution package. You need to adopt a consistent naming strategy for your test classes (and any additional test fixture classes), and filter them out of the final JAR file. Not too difficult, but an annoying extra step.
  2. The test code makes the source code file longer and potentially more confusing. You can minimize this by putting all the test code at the end of the file and collapsing the test classes if your editor allows it (we need #region for Java!).
  3. The test code pollutes the Javadocs. The only way I've found around it is to mark the test classes as @deprecated and omit deprecated classes when generating Javadocs. Optimally, I'd like to have a separate custom attribute (@TestClass?), and a Javadoc plugin that would skip over classes with this attribute. I couldn't do it in JDK 1.4, but JDK 1.5 might help in this area.

And that's it! Unless you're actively against keeping code and tests together, I don't think there's much to dislike about this approach. More importantly, I've tried it, and it works fine in practice. But don't take my word for it: look at the code yourself (all code is in src/java).