Extending SQLUnit: Writing your own tag handler

Overview

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:

  1. Write a Handler for your tag that implements the IHandler interface.

  2. Add the mapping of the tag name to the actual handler class in the etc/handlers.properties file

  3. Update the list of "swappable" tags in handlers.properties, if applicable.

  4. Modify the sqlunit.dtd file so that your new tag becomes part of the sqlunit dialect.

Note

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.

Finding your way around

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.

Warning

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

Package NameLocationDescription
net.sourceforge.sqlunitsrc/net/sourceforge/sqlunitThe 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.antsrc/net/sourceforge/sqlunit/antContains the Ant SQLUnit task
net.sourceforge.sqlunit.beanssrc/net/sourceforge/sqlunit/beansContains 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.handlerssrc/net/sourceforge/sqlunit/handlersContains all the handler implementations. All the implementations implement the IHandler interface.
net.sourceforge.sqlunit.typessrc/net/sourceforge/sqlunit/typesContains implementations of various types. All the implementations implement the IType interface.
net.sourceforge.sqlunit.matcherssrc/net/sourceforge/sqlunit/matchersContains some user-defined matcher classes for use with the diff tag. All matchers implement the IMatcher interface.
net.sourceforge.sqlunit.reporterssrc/net/sourceforge/sqlunit/reportersContains reporter classes that can be used with SQLUnit. All reporters implement the IReporter interface.
net.sourceforge.sqlunit.utilssrc/net/sourceforge/sqlunit/utilsContains utility classes whose methods get called by SQLUnit.
net.sourceforge.sqlunit.toolssrc/net/sourceforge/sqlunit/toolsContains tools to generate test cases from existing SQL statements using console input and GUI based input.
net.sourceforge.sqlunit.testtest/javaContains some Java code used for LOB testing.
net.sourceforge.sqlunit.test.mocktest/java/mockContains code for the mock database framework for testing SQLUnit code independent of a database.
Test files for various databasestest/*Contains subdirectories named after various databases and contains stored procedures and test XML files for these databases.
SQLUnit registry filesetc/*.propertiesMost 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.

Writing your handler

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.

Adding the tag to handler class mapping

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
          

Updating the list of valid swappable child tags

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
          

Adding the DTD for the new tag

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>