###################################################################### # # # This file is part of JUnitDoclet, a project to generate basic # # test cases from source code and helping to keep them in sync # # during refactoring. # # # # Copyright (C) 2002 ObjectFab GmbH (http://www.objectfab.de/) # # # # This library is free software; you can redistribute it and/or # # modify it under the terms of the GNU Lesser General Public # # License as published by the Free Software Foundation; either # # version 2.1 of the License, or (at your option) any later # # version. # # # # This library is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU Lesser General Public License for more details. # # # # You should have received a copy of the GNU Lesser General # # Public License along with this library; if not, write to the # # Free Software Foundation, Inc., 59 Temple Place, Suite 330, # # Boston, MA 02111-1307 USA # # # ###################################################################### Getting Started with JUnitDoclet ================================ We all want to deliver quality software. Therefore we do unit tests. For Java fans (like us) there is JUnit. It makes unit testing much more easy. (There are ports of it to many other languages. Just see http://www.junit.org/ .) Thanks to Kent Beck and Erich Gamma who created JUnit and to all the others who contributed. But still there are a few things we need to do "manually" with JUnit: * writing TestCases (Not just the content, but class headers, method headers, setUp and tearDown, ... all those things the compiler or the framework "need" as well. Who likes to do things, just because of a compiler "needing" them?! ;-) * coming up with names for test procedures (We don't want to waste creativity, right?) * creating TestSuites to organize our own TestCases (Did you ever miss to execute a TestCase which was there? Well, we did. The worst part of it: this TestCase was failing! You don't want that.) * ... Let's see, what we can do about it. * How about writing one TestCase per class? It wouldn't be as bad as it seems. If you want to add more TestCases / TestSuites (for example functional tests), that is still possible of course. The whole point of JUnitDoclet is to do repetitive tasks for you. If JUnitDoclet provides a TestCase skeleton for a class you didn't intend to test, it will be much less "pain" to check a few things anyway. * How about each of these TestCases having a test method for each public method of the corresponding class? * How about one TestSuite per package? * How about some help and guidance to keep the tests in sync with the application when doing Refactoring? That sounds like a monotone job. Computers are good at those, so we make the computer do it. Actually that is what JUnitDoclet is doing. But the fun (writing the content of the test methods) remains our job. Can you feel your productivity increasing already? Now, how does JUnitDoclet work? ------------------------------- We all know and admire JavaDoc. It parses our sources, gathering all sorts of information and finally writes all it's knowledge to HTML. Marvellous! Guess what, JavaDoc is even extendable! These extensions are called Doclets and they do the actual writing based on the information gathered. JUnitDoclet processes information about our own classes and packages and creates skeletons of TestCases and TestSuites. You can even specify a few details: * output directory (-d ) Note: This is required! This is, where the TestCases and TestSuites are written to (in sub directories according to their package of course). You may use a separate source tree for unit tests. This way you have your test cases in the same package as the classes they operate on, but at the same time, tests are separated from ordinary classes. Please don't mix tests and application classes! (There is a different way of separating tests from the application, but still you need to specify the output directory. See "sub package" for details.) * sub package (-subpackage ) If your IDE does not support several source trees, this option is for you. It defines a relative package, where to store test classes. Usually the relative package is "test". With that setting , the TestCase for a class root.sub.Sample would be root.sub.test.SampleTest and the TestSuite would be generated as root.sub.test.SubSuite. Please don't mix tests and application classes! (You could use a output directory other than the source directory. See "output directory" for details.) * build all (-buildall) Overwriting default behavior of "smart rewriting". By default, TestCases are regenerated only, if the source of the application class is newer then the source of the TestCase. And even if a file is regenerated, it won't be written if it equals the same file already there. To generate and write everything (TestCases and TestSuites) use the -buildall flag. * property file (-properties ) JUnitDoclet is customizable in many areas. Most important: the templates for test cases and test suites. If you are not happy with the default properties, make up your own definition and pass it on to JUnitDoclet. JUnitDoclet tries to load the file with this relative or absolute path. If this fails, it tries to load it as resource from class path. JUnitDoclet will let you know, which property file is in use. * naming strategy (-naming ) Defining names is difficult, especially if it has to be widely accepted. Don't worry, if you don't like our way, create your own and snap it in. * testing strategy (-testing ) If you are working on a large scale application with several million lines of source code, the template based approach may be to slow. If you want your style hard coded, just do it and plug it in here. * writing strategy (-writing ) Reading and writing files is quite simple. But if you want to access something else, than a regular file system (for example ftp or cvs) you write your own strategy and let JUnitDoclet know. * test in test (-testintest) If you want to generate TestCases for classes in a part of the source path which is output path too, this method enables that. (By default JUnitDoclet does not generate any tests for classes in the output directory. With -testintest enabled, only TestCases and TestSuites are excluded.) It is not recommended to use this option unless you really know what you are doing. Accessor tests -------------- Do you write tests for "set*" and "get*"? Do these methods fail sometimes? So why does not everyone write tests for them? If your accessor methods ("set"/"get" and "set"/"is") follow a few very basic rules, JUnitDoclet will help you. It will generate a combined test, provide an array with default values, call set* with every single value and check if get* returns it. With JUnitDoclet you get accessor tests for free (even for boolean kind of accessors like set*/is*). If you don't like, what JUnitDoclet generated, you can change the generated code. You can add values to the array or remove some of them. Because JUnitDoclet placed this default tests within markers, your modifications will remain. Tests are growing with the application -------------------------------------- Software development is very seldom pure top down. Instead, we constantly refactor our application and this is a good thing. By spec JUnitDoclet generates the TestCases and Suites based on the current application source. But while generating it can access the old TestCases as well. At some interesting places in each file, there are pairs of markers (at import, in each class and in each test method and -if you like that- around javadoc comments as well). Place your code/javadoc only within this marker pairs! All the code within marker pairs from an old TestCase will be merged into the new TestCase. All of it, but none of the code outside the pairs! This is very important, so please keep it in mind. When a method is renamed the name of the test method will change the next time you are generating. Fortunately marker pairs from the old TestCase without the same marker in the new TestCase are not lost. Instead they are placed in a special test method: "testVault". Just make sure to move the code to the test method where it belongs. JUnitDoclet will write a warning, if a "testVault" is not empty. With version 1.0 we changed the way markers are generated: Now the name(s) of the tested methods are in the marker, not the name of the test method. This way tools like IntelliJ IDEA can help you even more: When renaming a method during refactoring, comments (for example markers) are modified as well. When you are running JUnitDoclet the test will at it's place without the need to move it. (We felt, this benefit was worth the change in marker syntax from version 0.9.2 to version 1.0.) When renaming classes there is no such help within JUnitDoclet. Tools like IntelliJ IDEA offer some help, but (yet) can't do it all. Anyway, the old TestCase is still there for you. If you have any suggestion, about further help here, please let us know. Limits ------ JUnitDoclet does not solve all problems. There are some limits. For example it can't create an instance of an interface or an abstract class. But if they are used in a accessor... How could JUnitDoclet know, what to do? It just can't, and it won't confuse us by trying to. If generated TestCases don't compile when first generated, this is not a bug. Relax, make it compilable the way YOU want it and enjoy. Background: there is software on this planet, trying to solve problems for us, even if there are no problems. That kind of software is standing in the way. JUnitDoclet follows a different approach: "simple is beautiful." You should be glad to fix a few spots in generated code manually. This is much more predictable, than the results when trying to outsmart an assistant (or "evil wizard" as known from the book "The Pragmatic Programmer"). The down side of JUnitDoclet ---------------------------- Yes there is one, and it's not the fault of JUnit nor of JUnitDoclet. By using this tool you create lots of test methods. There are two extremes you have to avoid: * too few tests That is, if you get impressed by the high number of tests but most of the test methods are empty. Don't lie to yourself! Make test coverage more important to you then the number of tests! * too many tests Since it is so easy to write simple tests, you may get dragged away in writing millions of tests, but only for easy methods. Don't laugh, it happened. So don't forget to create some more advanced tests. They may be more difficult, but while being written they already help you to understand your system. Just don't forget to write application code as well, or you may get fired. ;-) There are no simple rules to decide how many tests are enough. Let's assume this: application classes and TestCases (!) need to be tested. TestCases test the application classes. And by doing that, indirectly the TestCases are proven to be working as well. Until you dig deeper into test coverage, let's compare sizes of tests and application. Rule of thumb: They should be about the same. In business projects this is often impossible because of time pressure. But managers will learn their lesson. Keep teaching and please do so in a kindly manner. ;-) License: -------- Please see lgpl.txt or GNU Lesser General Public License (www.gnu.org) Feedback --------- If you want to share your thoughts about JUnitDoclet feel free to write an Email to junitdoclet@objectfab.de . Please don't forget to see the other tools we provide on our website (www.objectfab.de). And now: H A V E F U N ! Kind Regards, Steffen Gemkow