This is getting asked a lot nowadays, so I thought I will write this up for the benefit of those who are interested in doing this. To include a new tag, you need to do three things:
Write a Handler for your tag that implements the IHandler interface.
Add the mapping of the tag name to the actual handler class in the etc/handlers.properties file
Update the list of "swappable" tags in handlers.properties, if applicable.
Modify the sqlunit.dtd file so that your new tag becomes part of the sqlunit dialect.
If you have written a TagHandler that does something unique, and would like to contribute it to the SQLUnit project, it would be gratefully accepted and credit given.
It may help a little if you are familiar with the way the code is laid out in the distribution, so its easy to find things. The following table provides the name, location and a brief description of each of the packages in use.
If you are already familiar with the pre-3.8 code, then you will find many changes in code for releases 4.0 and up. Hopefully, you will find that the changes are for the better. The code is (at least in my opinion) easier to read and understand, and better organized. Most of all, it should be easier to add new features as they are requested.
Table 67. SQLUnit Code Layout
|net.sourceforge.sqlunit||src/net/sourceforge/sqlunit||The main package which contains the classes which did not fit neatly into the sub-packages detailed below. This used to be the only package for SQLUnit.|
|net.sourceforge.sqlunit.ant||src/net/sourceforge/sqlunit/ant||Contains the Ant SQLUnit task|
|net.sourceforge.sqlunit.beans||src/net/sourceforge/sqlunit/beans||Contains beans to support the handlers. Older versions of SQLUnit handlers would return an Object from the process() method. They still return Objects, but now some of the handlers return well-defined Objects which can be cast to one of the beans in this package.|
|net.sourceforge.sqlunit.handlers||src/net/sourceforge/sqlunit/handlers||Contains all the handler implementations. All the implementations implement the IHandler interface.|
|net.sourceforge.sqlunit.types||src/net/sourceforge/sqlunit/types||Contains implementations of various types. All the implementations implement the IType interface.|
|net.sourceforge.sqlunit.matchers||src/net/sourceforge/sqlunit/matchers||Contains some user-defined matcher classes for use with the diff tag. All matchers implement the IMatcher interface.|
|net.sourceforge.sqlunit.reporters||src/net/sourceforge/sqlunit/reporters||Contains reporter classes that can be used with SQLUnit. All reporters implement the IReporter interface.|
|net.sourceforge.sqlunit.utils||src/net/sourceforge/sqlunit/utils||Contains utility classes whose methods get called by SQLUnit.|
|net.sourceforge.sqlunit.tools||src/net/sourceforge/sqlunit/tools||Contains tools to generate test cases from existing SQL statements using console input and GUI based input.|
|net.sourceforge.sqlunit.test||test/java||Contains some Java code used for LOB testing.|
|net.sourceforge.sqlunit.test.mock||test/java/mock||Contains code for the mock database framework for testing SQLUnit code independent of a database.|
|Test files for various databases||test/*||Contains subdirectories named after various databases and contains stored procedures and test XML files for these databases.|
|SQLUnit registry files||etc/*.properties||Most of SQLUnit is interface driven, and uses Factory classes to instantiate a named implementation of an interface. The registry files contain the mappings to the various implementations.|
Your handler must implement the net.sourceforge.sqlunit.IHandler interface. It should also declare a default (null) constructor, either implicitly or explicitly. The only method to be overridden is the process() method with the following signature.
public java.lang.Object process(org.jdom.Element el) throws java.lang.Exception;
A tag defines an action that must be performed on the element. When the SQLUnit class parses the test XML document in its processDoc() method, it looks up the tag name, then uses the handlers.property resource file to instantiate the appropriate handler and call its process() method with the current JDOM Element for that tag. There is no restriction on the class the handler returns from its process() method. Some handlers return null, some return a java.sql.Connection object (ConnectionHandler), and yet others return a net.sourceforge.sqlunit.DatabaseResult object (SqlHandler, CallHandler, etc). Handlers may call other handlers corresponding to its child tags in the same manner, using the following code template.
IHandler handler = HandlerFactory.getInstance(element.getName());
Ideally, there should be no check on which handler is allowed to instantiate which child element, since this can be easily coded into the SQLUnit DTD. In reality, you will find instances of where this is so. These are either bad design decisions on my part and are candidates for refactoring, or its just too hard to put into a DTD.
As it pulls out data from the element, the handler would do something to process it and convert it into some other data structure. An example of it is the SqlHandler, which pulls out the SQL statement and positional parameters, then executes the query against the connection in the registry. It then converts the resultset retrieved into a DatabaseResult object and returns it.
This is a single line entry into the etc/handler.properties file, which can be found in the src/net/sourceforge/sqlunit subdirectory of the distribution. You will need to run the ant compile target for it to be available under your build subdirectory where subsequent calls to the sqlunit ant task can find it.
mytag = net.sourceforge.sqlunit.MyTagHandler
Some tags have the notion of "swappable" child tags. Not all parent tags contain swappable child tags. Currently the only ones defined are sqlunit, test and batchtest. Swappable tags are defined as multiple elements in an OR relationship in the DTD. A snippet showing the swappable tags in the handlers.properties are shown below.
sqlunit.swappable.tags = test, batchtest, diff test.swappable.tags = sql, call, methodinvoker, dynamicsql batchtest.swappable.tags = batchsql, batchcall
The DTD entry for your new tag will specify what are the legal attributes and subelements of your new tag. An example of the DTD for a simple tag with two attributes is shown below. For more complex tags, see the sqlunit.dtd file in the docs/ subdirectory.
<!ELEMENT mytag EMPTY> <!ATTLIST mytag attr1 CDATA #REQUIRED attr2 CDATA #REQUIRED>