######################################################################
# #
# 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