SQLUnit User Guide

Sujit Pal


Table of Contents
Introduction
Alternative Forms of this document
Printer Friendly Format
Downloadable Format
Historical Trivia
Porting Information
License Information
Installing
Downloading the source
Required JAR files
Running the install script
Running
SQLUnit Command Line
Interpreting the output
SQLUnit GUI Tool
SQLUnit TUI Tool
SQLUnit XSL Transform Tool
SQLUnit TestDocsGenerator Tool
SQLUnit Canoo2HTML Tool
SQLUnit Tags
List of currently supported tags
The sqlunit Tag
The setup Tag
The skip Tag
The struct Tag
The subparam Tag
The batchresult Tag
The call Tag
The set Tag
The classifiers Tag
The jndi Tag
The methodinvoker Tag
The teardown Tag
The foreach Tag
The connection Tag
The func Tag
The echo Tag
The include Tag
The result Tag
The methodArgs Tag
The row Tag
The param Tag
The funcdef Tag
The batchsql Tag
The prepare Tag
The typedef Tag
The severity Tag
The paramset Tag
The subdef Tag
The col Tag
The batchtest Tag
The constructorArgs Tag
The batchcall Tag
The outparam Tag
The typemap Tag
The test Tag
The resultset Tag
The match Tag
The field Tag
The sub Tag
The exception Tag
The diff Tag
The category Tag
The arg Tag
The sql Tag
The dynamicsql Tag
Extending SQLUnit: Writing your own tag handler
Overview
Finding your way around
Writing your handler
Adding the tag to handler class mapping
Updating the list of valid swappable child tags
Adding the DTD for the new tag
Getting Connections
Basic JDBC Connection
JDBC Connection with properties
Basic JNDI Connection
JDBC Connection using local JAR file
Connection Properties from external file
Passing in a Connection object to SQLUnit
Controlling Transactions
Using variables
Setting explicitly from scalar
Setting explicitly from query
Setting explicitly from Java method call
Setting variables from ant
Setting variables implicitly
Including Files
Including XML Files
SQLUnit Include Files
Error Messages
Supported Datatypes
List of currently supported datatypes
Extending SQLUnit: Adding support for your own datatypes
Using an existing implementation for an unlisted datatype
Overriding the implementation for a datatype
Adding a new implementation for a new datatype
SQLUnit Assertions
List of currently supported Assertions
Extending SQLUnit: Adding your own Assertion
Large Object (LOB) Support
Handling Object output from ORDBMS
Handling BLOB and CLOB outputs
Java Object Support
User-Defined Matching
What is User-Defined Matching
Wildcarding rules for Match Attributes
List of currently supported Matchers
Extending SQLUnit: Writing your own Matcher
Setting up your Matcher for use with SQLUnit
User-Defined Reporting
What is User-Defined Reporting?
List of currently supported Reporters
Extending SQLUnit: Writing your own Reporter
Setting up your Reporter for use with SQLUnit
User-Defined Test Grouping and Test Skipping
What are User-Defined Test Groups
Declaring the group of a test
SeverityHandler
CategoryHandler
Extending SQLUnit: Write your own classification
Skipping Tests with <skip>
Using the Mock Testing Framework
Overview
Writing your own mock procedures
Writing tests against the mock procedures

Introduction

SQLUnit is a regression and unit testing harness for testing database stored procedures. The test suite is written as an XML file. The SQLUnit harness itself is written in Java and uses the JUnit unit testing framework to convert the XML test specifications to JDBC calls and compare the results generated from the calls with the specified results.

Stored procedures are often used to provide an object abstraction to the underlying relational data store. The object abstractions are consumed by Java middleware programs. In shops where this is the case, Java programmers often use the popular JUnit testing framework to unit test their code. Using SQLUnit to unit test the database stored procedures thus becomes a natural extension of the unit testing process, and provides unit testing coverage for a layer that is often neglected in this respect.

Since the stored procedure author need not be a Java coder, and may not even know Java or JUnit, SQLUnit provides an XML language to write test suites. Stored Procedure authors who know Java and/or can write JUnit tests will find that the repetitive nature of JDBC programming would, over time, encourage refactoring the common parts of the tests into utility classes. SQLUnit already does this, except it goes one step further and abstracts the test specification out of Java into XML.

The XML itself is fairly small and easy to learn for anybody and should be instantly intuitive to a person who is already familiar with JUnit.

Further, since SQLUnit uses JDBC to communicate with the database, it should be possible to write unit tests for stored procedures for any database that provides a JDBC driver. Some relatively painless porting may be needed for each new DBMS or driver, due to vendor-specific extensions to the SQL standard or restrictions of the JDBC API.


Alternative Forms of this document


Printer Friendly Format

This is the same document as this one, but comes as one large HTML file, which is suitable for printing and reading where you may not have access to a computer.

You can use the File::Print command from the browser menu to send the document to the printer.

Print User Guide.


Downloadable Format

This is a tar.gz file which can be downloaded to your personal computer, so you dont have to connect to the network every time you want to read the SQLUnit documentation. You will have to make a subdirectory and gunzip and untar inside that subdirectory.

Here is the sequence of instructions (for Unix/Linux users) to build the documentation locally.


$ mkdir sqlunit-docs
$ mv /download/dir/htdoc.tar.gz sqlunit-docs
$ cd sqlunit-docs
$ tar xvzf htdoc.tar.gz

You can access this from the browser using a file URL from your browser, like so:


file:///path/to/sqlunit-docs/index.html

Make sure to check back from time to time, though, since there may have been bug fixes and feature enhancements in future releases that you could probably take advantage of.

Download User Guide.


Historical Trivia

This project was inspired by a regression testing tool for Informix stored procedures, originally written by Mark H Sullivan. It was written using a combination of Java, shell scripts and make, and supported a very rudimentary set of directives for running the stored procedures in a certain sequence and comparing the outputs with the specified outputs. All error checking was at runtime, so we had to be careful to avoid "syntax" errors such as using double quotes instead of single quotes. It was also very hard to extend, as I found out when trying to add some new directives.

I re-wrote this tool in C using lex and yacc for the command processor, and Informix ESQL/C to call the procedures themselves. This fixed some of our problems, because the problems associated with debugging missing quotes and newlines were caught earlier on. However, the tool would mysteriously segfault occasionally. Some leaks were caught with dbmalloc, but the problems still remained, so we had to abandon the tool as well.

I re-wrote the tool again using Perl. It used the same input files as the original tool and the C tool, and provided early error checking similar to the C tool. It also provided more intuitive error messages when the tests failed.

Some months later, on another project, I was introduced to JUnit based on a chance remark of a colleague, and software development has never been the same for me since. I was almost an instant JUnit convert.

Some years later, on yet another project, I learned how to associate Java handler classes with XML tags, which forms the basis of much of the architecture of SQLUnit.

Over the years, and especially in my current job, I have been exposed to interface driven development and the use of factories to instantiate implementation classes, which also form the basis of the SQLUnit design.

I started SQLUnit as a Sourceforge project when I was tasked with writing the persistence layer (including development of Sybase stored procedures) for a new application. While JUnit sufficed to test the Java portion of the code, I missed having a regression tool for stored procedures around. So based on what I thought would be useful, and on the lessons learned during the last few iterations of building a similar tool, I set about writing one in my spare time (on my train ride to and from work). I ultimately ended up delivering the project without using SQLUnit, but I hope to use it the next time I have to write a similar application.

SQLUnit would be different from its predecessors in that it would be based on a standard testing framework (Junit), have a clean, expressive and extendable command syntax (XML), and be able to run on any database which provided a JDBC driver.

SQLUnit in its first incarnation only provided for exact equality comparisons between specified results and the results of SQL or stored procedure calls. It used JDBC to get a database connection. Over time, SQLUnit has evolved, so we now have around 6 different ways of getting a database connection, including one by looking up a named DataSource from a JNDI server. The original code only had around 10 tags, now we have around 30.

SQLUnit has been put to various uses by its user community, apart from the one for which it was intended, namely regression testing stored procedures. It has been used to regression test new JDBC drivers against old ones and against a known set of specifications. It has been used to validate ETL runs from operational databases to a data warehouse with a specified degree of accuracy. It has been bundled along with testing suites for commercial applications.

SQLUnit has benefited tremendously from the feedback and criticism from the community, many of which were very insightful and knowledgeable, and have formed the basis of enhancements and features to SQLUnit. It has also benefited from the numerous patches, bug fixes and porting efforts by various members of the community. In short, SQLUnit is what it is today because of the strong community support that it has. Thank you, SQLUnit would not be what it is today without you.


Porting Information

Work is currently going on to port SQLUnit (or to just make it run on) various databases. Here is the status of the porting efforts till date. If you are running SQLUnit with databases other than those listed below, let us know, and we will add in your information as well. If you are planning to run SQLUnit with a database not listed below, you probably will have no problems. Most non-standard features (such as Oracle CURSORs, multiple resultsets in Sybase, etc) have been identified and accounted for in the SQLUnit code. In case you do have problems, however, please let us know on the forum.

Table 1. Ports of SQLUnit to different databases

Database Name (Version)PorterStatus
PostgreSQL 7.3Sujit PalComplete. PostgreSQL was used to do a proof of concept of the initial release of SQLUnit. It has been used thereafter to test new features as they are developed.
MySQL 3.4Sujit PalMySQL does not support stored procedures or functions, but it has been used to test the batch tags, the diff and the sql tags. It plays nicely with SQLUnit. MySQL will support stored functions tentatively in version 5, so we will revisit this when that happens.
OracleSahib Singh Wadhwa, Andrei Grigoriev, Sujit PalComplete, with a lot of patches and bug reports from Sahib and Andrei. Thanks to Andrei for running SQLUnit through a very thorough testing with Oracle and getting the last few Oracle CURSOR specific bugs ironed out.
Sybase ASE 12.5Sowmya Shetty, Lakshmi, Sujit PalComplete, with lots of testing support from Soumya Shetty and Lakshmi
IBM DB2Sujit PalStarted June 05, 2003. In progress. The stored procedures were written in C, the Java stored procedures had issues running under JDK 1.4. Still need to build a test suite and run them.
HSQLDB (formerly Hypersonic)Campbell Boucher-BurnettIn progress
Informix SE 7.0-Not started. This should work, but has not been tested. The test cases are available in CVS, but the JDBC driver that I had available had issues with working with PAM authentication on Linux. If someone has a working setup of Informix, then I would appreciate you running the tests and letting me know the results.
Microsoft SQL Server 2000James Henderson, Paul KingOn May 29, 2005, James contributed his suite of MS SQL Server tests that he uses to regression test SQLUnit. Earlier, on May 20, 2004, Paul reported that he has been using SQLUnit to test MS SQL Server 2000 for some time, but has not tested the LOB feature yet.
Sybase ASA 9.0.1David FishburnComplete, test suite included with distribution (test/asa/README)


License Information

 

English is broken, it does not distinguish between free beer and free speech. Spanish on the other hand, distinguishes between gratis and libre. Free software is libre software.

 
--Miguel de Icaza 

SQLUnit is libre software. It is also gratis software, but if someone wants to earn money by installing, customizing or training people about it for a fee, I dont see any problems with that. SQLUnit is distributed under the terms of the GNU General Public License, which gives you the right to copy, modify and re-distribute it either in source or binary form. Using SQLUnit does not obligate you to contribute back to the project any enhancements or fixes you make to it, but we appreciate the gesture if you do so.

The full text of the GNU General Public License can be found here.


Installing


Downloading the source

The latest source code can be downloaded from sourceforge via anonymous cvs. The CVSROOT environment will need to be set as follows (for Linux using bash):


$ export CVSROOT=:pserver:anonymous@cvs.sqlunit.sourceforge.net:/cvsroot/sqlunit
$ cvs -z3 checkout sqlunit

Alternatively, you can download the latest released tarball (which also contains the source, but may not be the latest) from the download section. If you are going to be doing any development work for SQLUnit, however, I would recommend getting the source from CVS.

Warning

Note that tarballs prior to version 1.4 cotained only the SQLUnit JAR file due to a packaging bug, and are hence binary only. Additionally, you would have to go to CVS to pick up the DTD file. Thanks to Arjen Stolk for pointing this out.


Required JAR files

Table 1. JARs used by SQLUnit

Software and VersionHow it is usedLicensingURLRequiredIncluded
Java 1.4 or laterSQLUnit is written using Java 1.4_02, so you will need to install the JRE if you are just going to use it, or the JDK if you are going to do development with it.Proprietary but free for download and usehttp://java.sun.comYesNo
Ant 1.6 or laterAnt is used to build the SQLUnit package. An ant task is provided to run SQLUnit from within Ant.Apache Licensehttp://ant.apache.orgYes, although you can also just call it from the command line with java.No
JUnit 3.7 or laterSQLUnit is really a specialized JUnit test class. It is needed to run SQLUnit.Common Public License Version 1.0http://www.junit.org/index.htmYesYes
JDOM 1.0 or laterSQLUnit uses JDOM to do its XML parsing and rendering.Apache Licensehttp://www.jdom.orgYesYes
Apache Log4J 1.2.8 or higherThis is used for logging in SQLUnit.Apache Licensehttp://logging.apache.org/log4j/docsYesYes
Apache Xerces-J 1.4.4Used by Canoo WebTesthttp://xml.apache.org/xerces-j/YesYes 
MockRunner 0.2.7SQLUnit uses this to set up and run a set of Mock tests against a Mock database that consists of method calls which are introspected.Apache style licensehttp://mockrunner.sourceforge.netNo. This is only needed if you want to run the mock test suite to make sure everything worked.Yes
Apache Commons-LangUsed by MockRunnerApache Licensehttp://jakarta.apache.org/commons/No, see above.Yes
XDoclet 1.2.1SQLUnit uses XDoclet to convert source code annotations into Docbook XML which is then included in this document.BSD Licensehttp://xdoclet.sourceforge.netNo, only needed to generate the documentation.No
Apache Jalopy 0.6.2SQLUnit uses Jalopy to provide a guide for coding conventions used in SQLUnit. Contributed code typically passes through Jalopy to get reformatted before inclusion.BSD Licensehttp://jalopy.sourceforge.netNo, only needed to reformat contributed code before submission.No
Apache Checkstyle 3.4SQLUnit uses CheckStyle to audit code style on an ongoing basis. The checks that have been configured are a union of the sun_checks.xml and BlochEffectiveJava.xml files supplied with the distribution. Some checks have been relaxed or eliminated where it did not match the coding style for SQLUnit.LGPLhttp://checkstyle.sourceforge.netYes, if you plan on making code contributions.No
JavaCC 3.2SQLUnit uses JavaCC to generate a parser for include files based on the grammar specified in the IncludeFileParser.jj.BSD Licensehttps://javacc.dev.java.netYesYes
Apache Commons JEXL 1.0SQLUnit uses JEXL as the Expression parser and evaluator for its ExpressionMatcher matcher.Apache Licensehttp://jakarta.apache.org/commons/jexl/YesYes
Apache Commons Logging 1.0JEXL needs Apache Logging as a runtime dependency.Apache Licensehttp://jakarta.apache.org/commons/logging/YesYes

Warning

JAR files for open source databases, such as MySQL and PostgreSQL, which used to be supplied with the distribution, have been removed. This is because SQLUnit has shifted to using the MockRunner project to build its own mock database to run its tests instead of running them against popular databases. Users of databases already have the corresponding JDBC drivers installed, so providing them did not make much sense anyway.


Running the install script

If you want to install SQLUnit from source, you can run the following commands.


$ ant

Buildfile: build.xml

init:

help:
     [echo] SQLUnit version 4.9
     [echo] Targets:
     [echo]   help - display this message
     [echo]   clean - removes all files from the build directory
     [echo]   distclean - removes all files from the dist directory
     [echo]   compile - compile all the java files
     [echo]   compile-test - compiles java files needed for testing
     [echo]   test - runs the mock database tests
     [echo]   junit-test -Dtest.class=classname - runs JUnit test
     [echo]   reformat - formats sources in contrib with Jalopy
     [echo]   checkstyle - check code style using CheckStyle
     [echo]   install - creates the jar files in the dist directory
     [echo]   sqlunit-flat -Dtestfile=file.xml - runs SQLUnit
     [echo]   sqlunit-nested -Dtestdir=dir - runs SQLUnit w/FileSet
     [echo]   javadoc - creates the project javadocs
     [echo]   doclet - generates Docbook XML for sqlunit tags
     [echo]   htdoc - creates tarball for website
     [echo]   package - creates tarball for download
     [echo]   gui - start up the SQLUnit GUI Tool
     [echo]   tui - runs the SQLUnit TUI Tool for testing

BUILD SUCCESSFUL


      

So to create a SQLUnit JAR file from scratch, run the following ant directive to create the sqlunit.jar file in your dist directory.


$ ant install

Buildfile: build.xml

setup:

clean:
   [delete] Deleting directory /home/spal/sqlunit/build
    [mkdir] Created dir: /home/spal/sqlunit/build

compile:
    [javac] Compiling 18 source files to /home/spal/sqlunit/build
     [copy] Copying 1 file to /home/spal/sqlunit/build/net/sourceforge/sqlunit

distclean:
   [delete] Deleting directory /home/spal/sqlunit/dist
    [mkdir] Created dir: /home/spal/sqlunit/dist

install:
      [jar] Building jar: /home/spal/sqlunit/dist/sqlunit-1.0.jar
     [echo] File sqlunit-1.0.jar in dist

BUILD SUCCESSFUL

Total time: 3 seconds

      

Running


SQLUnit Command Line

SQLUnit is really a single JUnit test which reads the SQLUnit test XML file to determine what it should do. Starting with version 1.8, the SQLUnit Ant task comes with new configuration options.

Since the sqlunit task is an optional task, the task must be first declared to Ant using a taskdef, like this.


<!-- This is specified once per buildfile -->
<target name="def">
  <taskdef name="sqlunit" 
       classname="net.sourceforge.sqlunit.ant.SqlunitTask">
    <classpath>
      <pathelement location="/your/location/for/sqlunit.jar" />
    </classpath>
  </taskdef>
</target>

In order to run a single test file called test/mysql/test.xml from within Ant using the sqlunit task, the XML snippet in the build file would look like this.


<!-- This is repeated for each test or 
     group of tests in case of nested filesets -->
<target name="run-postgres-test" depends="def">
  <sqlunit testfile="test/postgres/test.xml" 
      haltOnFailure="false" debug="false" />
</target>

Notice the new attributes haltOnFailure and debug. Both of them are optional attributes and SQLUnit will revert back to its default behavior if they are not specified. We will discuss them in more detail below.

In cases where you would like to run a group of SQLUnit test files, you can use nested filesets instead of the testfile attribute. So if your build base directory was the same as your stored procedure repository root, say /opt/database, under which you have organized your stored procedures into different subdirectories based on function, something like this:


/opt/database
 |-- stored_procedures
 |    |
 |    |-- accounting
 |    |-- human_resources
 |    |-- infotech
 |    |-- ...
      

Further, let us assume that you store your SQLUnit test XML files for each functional silo along with the stored procedures, so your accounting SQLUnit XML test file is /opt/database/accounting/accounting_tests.xml. To specify that you wish to run all the .xml under the stored procedures directory, you would specify the sqlunit target like this:


<target name="run-all-test" depends="compile,def">
  <sqlunit haltOnFailure="false" debug="false">
    <fileset dir="stored_procedures">
      <include name="**/*.xml" />
    </fileset>
  </sqlunit>
</target>

You can specify multiple nested fileset elements if needed. Just remember that the testfile attribute specification and the nested fileset specifications are mutually exclusive. SQLUnit will give you an error if you attempt to specify both.

The haltOnFailure and the debug attributes can be used to control the behavior of SQLUnit. This is described below.

Table 1. Configuring SQLUnit behavior

Attribute NameValid ValuesDescription
haltOnFailure"true" or "false", default "false"If set to "true", SQLUnit will stop executing the current test file if there is a test failure or exception. If multiple files are specified using nested fileset elements, then it will continue to the next file.
debug"true" or "false", default "false"If set to "true", haltOnFailure will also be set to "true" regardless of the value set. Setting debug to "true" will output a very verbose trace log on the console. The trace contains a line for every method entered while the test was running.
logfileA valid file nameIf not set or set to an empty string, the logging for SQLUnit will be sent to STDOUT. If set, it will be sent to the file specified. The filename can be absolute or relative to the directory where the ant command is being run. This feature has been available since 2.2
logformatFormat name identifying a reporterIf not set or set to an empty string, the default reporter would be used. The default reporter will print a formatted report of the tests that were executed. This feature is available starting with version 3.6

If the logfile attribute is not set, SQLUnit will log its output to STDOUT and its exceptions, if any, to STDERR. This behavior has been introduced since version 2.2. Scripts running SQLUnit versions prior to this and depending on the older behavior (all logging to STDERR) will need to change the scripts to add logging to STDOUT as well).

Warning

SQLUnit will report all its status messages, including error messages to STDOUT. If there were one or more failures at the end of the build, then it will send a BuildException to Ant which will report a BUILD FAILED message to the user.

Starting with version 2.2, SQLUnit can be used with Ant's own BuildLoggers. The code has been tested with the org.apache.tools.ant.DefaultLogger and org.apache.tools.ant.XmlLogger loggers. To use a specific buildlogger, you can either use Ant's record tag or supply the log file name and logger class name in the ant command line, like so:


ant -logfile ${logfile_name} -logger ${logger_class_name} ${target}

Note

Do not specify a logfile attribute in the sqlunit task when specifying the logfile and logger on the command line. This can lead to unexpected behavior.

Alternatively, you can use either the depends attribute of target or the antcall task to run more than one suite of tests with a single call. However, using the nested fileset elements in the sqlunit task seems to be the more obvious way to do it.

Warning

The options described for running SQLUnit from the command line using java or using the junit task under Ant in the documentation for versions prior to version 1.8 are deprecated and no longer supported. These approaches were cumbersome and not widely used.


Interpreting the output

A clean SQLUnit run will look something like the example below. This is the output from the test suite. Notice that the ant target sqlunit uses the standard JUnit output formatter but only writes its log to STDOUT, so its output is all grouped neatly in one place.


$  ant sqlunit -Dtestfile=test/postgresql/test.xml

Buildfile: build.xml

init:

test-ant-task:
  [sqlunit] Getting connection...
  [sqlunit] Setting up test...
  [sqlunit] Running test[1]: Adding department HR
  [sqlunit] Running test[2]: Adding department InfoTech using non-Callable form
  [sqlunit] Running test[3]: Adding Employee John Doe to InfoTech
  [sqlunit] Running test[4]: Adding John Doe again
  [sqlunit] Running test[5]: Adding Jane Doe to HR
  [sqlunit] Running test[6]: Adding Dick Tracy to InfoTech
  [sqlunit] Running test[7]: Updating Hourly Rate for John
  [sqlunit] Running test[8]: Looking up John Doe by name
  [sqlunit] Running test[9]: Looking up all employees in InfoTech
  [sqlunit] Running test[10]: Adding timecard for John
  [sqlunit] Running test[11]: Adding another timecard for John
  [sqlunit] Running test[12]: Adding timecard for Dick
  [sqlunit] Running test[13]: Getting monthly report for InfoTech
  [sqlunit] Tearing down test...
  [sqlunit] .
  [sqlunit] Time: 3.795
  [sqlunit]
  [sqlunit] OK (1 tests)
  [sqlunit]

BUILD SUCCESSFUL

Total time: 4 seconds

By default, SQLUnit will not consider a test failure as sufficient reason to stop the test suite from completing, since a test suite can contain unrelated tests that may not depend on each other. If you need SQLUnit to stop processing the suite after it encounters a test failure, set the haltOnFailure attribute to true. Here is an example of SQLUnit generating an error, with inline pointers to comments.


Buildfile: build.xml

init:

test-ant-task:
  [sqlunit] Getting connection...
  [sqlunit] Setting up test...
  [sqlunit] Running test[1]: Adding department HR
  [sqlunit] Running test[2]: Adding department InfoTech using non-Callable form
  [sqlunit] Running test[3]: Adding Employee John Doe to InfoTech
  [sqlunit] Running test[4]: Adding John Doe again
  [sqlunit] Running test[5]: Adding Jane Doe to HR
  [sqlunit] Running test[6]: Adding Dick Tracy to InfoTech
  [sqlunit] Running test[7]: Updating Hourly Rate for John
  [sqlunit] Running test[8]: Looking up John Doe by name
  [sqlunit] Running test[9]: Looking up all employees in InfoTech
  [sqlunit] Running test[10]: Adding timecard for John
  [sqlunit] Running test[11]: Adding another timecard for John
  [sqlunit] Running test[12]: Adding timecard for Dick
  [sqlunit] Running test[13]: Getting monthly report for InfoTech
  [sqlunit] No match on variable at [rset,row,col]=([1,2,4] (1)
  [sqlunit] *** expected: (2)
  [sqlunit] <result>
  [sqlunit]   <resultset id="1">
  [sqlunit]     <row id="1">
  [sqlunit]       <col id="1" type="VARCHAR">Information Technology</col>
  [sqlunit]       <col id="2" type="VARCHAR">Dick Tracy</col>
  [sqlunit]       <col id="3" type="INTEGER">13</col>
  [sqlunit]       <col id="4" type="NUMERIC">50.00</col>
  [sqlunit]       <col id="5" type="NUMERIC">650.00</col>
  [sqlunit]     </row>
  [sqlunit]     <row id="2">
  [sqlunit]       <col id="1" type="VARCHAR">Information Technology</col>
  [sqlunit]       <col id="2" type="VARCHAR">John Doe</col>
  [sqlunit]       <col id="3" type="INTEGER">16</col>
  [sqlunit]       <col id="4" type="NUMERIC">56.00</col (3)
  [sqlunit]       <col id="5" type="NUMERIC">880.00</col>
  [sqlunit]     </row>
  [sqlunit]   </resultset>
  [sqlunit] </result>
  [sqlunit] *** but got: (4)
  [sqlunit] <result>
  [sqlunit]   <resultset id="1">
  [sqlunit]     <row id="1">
  [sqlunit]       <col id="1" type="VARCHAR">Information Technology</col>
  [sqlunit]       <col id="2" type="VARCHAR">Dick Tracy</col>
  [sqlunit]       <col id="3" type="INTEGER">13</col>
  [sqlunit]       <col id="4" type="NUMERIC">50.00</col>
  [sqlunit]       <col id="5" type="NUMERIC">650.00</col>
  [sqlunit]     </row>
  [sqlunit]     <row id="2">
  [sqlunit]       <col id="1" type="VARCHAR">Information Technology</col>
  [sqlunit]       <col id="2" type="VARCHAR">John Doe</col>
  [sqlunit]       <col id="3" type="INTEGER">16</col>
  [sqlunit]       <col id="4" type="NUMERIC">55.00</col> (5)
  [sqlunit]       <col id="5" type="NUMERIC">880.00</col>
  [sqlunit]     </row>
  [sqlunit]   </resultset>
  [sqlunit] </result>
  [sqlunit] Tearing down test...
  [sqlunit] .
  [sqlunit] Time: 1.204
  [sqlunit]
  [sqlunit] OK (1 tests)
  [sqlunit]

BUILD SUCCESSFUL

Total time: 2 seconds
(1)
The error message says that there is a difference in resultsetId = 1, rowId = 2, colId = 4
(2)
This is what the test expected. This is supplied to SQLUnit by the test author in the XML file.
(4)
This is the result object that SQLUnit generated by running the SQL or stored procedure.
(3)
This is the place in the expected result where the difference was detected.
(5)
This is the place in the generated result which is different from the expected result. This looks like a simple typo, so our corrective action would be to correct the test specification. Other kinds of errors may require us to fix the stored procedure code and re-run the test.

SQLUnit GUI Tool

This tool was originally contributed by Mohan Iyer. Arun Viswanath has done some major rework to add many more features to this tool.

The GUI Tool is a Swing based application for generating test cases by running the stored procedure against the database. The tool will append to the capture file defined in the properties file for the application. This is a very basic tool, it does not have any elegant error handling, but it works. The original version of the tool was contributed by Mohan Iyer, who used it to generate SQLUnit test cases against an Oracle database. The tool has been changed since to be more database agnostic.

To start the tool from the command line using, you can use the ant gui command defined in the supplied build.xml file. Alternatively you can set up a batch file or shell script to include the SQLUnit and JDOM JAR files in your classpath and invoke the following command:


$ ant gui [-Dgui.rcfile=etc/guirc.properties]

Here are some screenshots of the SQLUnit GUI Tool.

Figure 1. GUI Tool Startup Screen

Figure 2. GUI Tool with data

A sample guiconfig.properties is provided in the etc directory in the distribution. Please modify it to suit your own installation.

Note

Arun Viswanath has made many improvements to the GUI Tool, since Mohan Iyer's original contribution, and my subsequent effort to make the tool database agnostic. If you have suggestions for improvement for the GUI Tool, please let him know by posting to the forum.

Note

Mohan Iyer envisaged that this basic Swing application could be leveraged to provide various editor plug-ins for SQLUnit. If anyone is interested in doing this, please feel free to go ahead and send me the plug-in code and I will be happy to put it in CVS and give you credit. Since I dont use IDEs myself, I will just take your word for it that the plug-in works.


SQLUnit TUI Tool

This tool was originally contributed by Sahib S Wadhwa for use with Oracle databases and subsequently modified by Sujit Pal to make it more database agnostic.

The TUI tool is a console based tool for manufacturing test cases by running the stored procedure against the database. The tool will generate the XML for the test case, which can then be cut and pasted into the test specifications. The tool eliminates the drudgery of having to write the XML test cases by hand, speeding up the test building process enormously. It also has a new interactive interface similar to text based database client tools. The inputs can be either partially or fully driven by the contents of a Java properties file, thereby making it partially or completely non-interactive if desired.

Here is a sample run of the TUITool. Make sure that the JDBC Driver for the database you are using, the JDOM JAR file and the SQLUnit JAR file is in your CLASSPATH. Alternatively, set the CLASSPATH on the java command line with -cp.


$ ant tui [-Dtui.mode=offline] [-Dtui.rcfile=etc/tuirc.properties]

SQLUnit TUI Tool
Copyright(c) 2003 The SQLUnit Team
TUITool:_captureFile> /home/sujit/sqlunit/tui.out
TUITool:_connection!driver> com.mysql.jdbc.Driver
TUITool:_connection!url> jdbc:mysql://localhost:3306/sqlunitdb
TUITool:_connection!user> defaultuser
TUITool:_connection!password> defaultuser
TUITool:__test!name> Checking returned value from customer
TUITool:__test!call!stmt> select custId from customer where custId=?
TUITool:___test!call!param[1]!type> INTEGER
TUITool:___test!call!param[1]!inout> in
TUITool:___test!call!param[1]!is-null> false
TUITool:___test!call!param[1]!value> 1
<test name="Checking returned value from customer">
  <sql>
    <stmt>select custId from customer where custId=?</stmt>
    <param id="1" type="INTEGER" inout="in" is-null="false">1</param>
  </sql>
  <result>
    <resultset id="1">
      <row id="1">
        <col id="1" type="INTEGER">1</col>
      </row>
    </resultset>
  </result>
</test>

Output captured to /home/sujit/sqlunit/tui.out
TUITool:__test!name> q
$ 

The -Dmode=offline indicates that all replies to the prompts are stored in the supplied properties file indicated by -Drcfile. Offline mode can be used to run only a single test and is totally non-interactive. When the interactive mode is desired, some things such as the connection information, can be stored in the properties file to minimize typing effort.

The properties file is a standard Java properties file. A sample properties file is included in the distribution in etc/tuirc.properties. This will need to be modified and copied to whatever you want to call your rcfile for the SQLUnitTUI Tool. The keys mimic the prompts that the tool provides.


SQLUnit XSL Transform Tool

If your SQL or stored procedures that you want to test are already available in an XML format, if you were using a ORM framework such as iBATIS SQLMaps, for instance, then you can use this tool to transform the information into a form suitable for SQLUnit to consume. The Transform Tool is a simple XSLT parser. You will need to supply the appropriate XSL file containing the required transformation.

The W3Schools page on XSLT is quite comprehensive and is useful as a reference or tutorial if you need pointers on how to get started using XSLT.

A sample run of using the TransformTool from within ant is shown below:


$ ant transform -Dtransform.input=inputfile.xml \
    -Dtransform.transform=xsltfile.xsl \
    -Dtransform.output=sqlunitfile.xml

Buildfile: build.xml
 
init:
  
precompile:
   
compile:
    [javac] Compiling 10 source files to /home/sujit/src/sqlunit/build
    [javac] Compiling 1 source file to /home/sujit/src/sqlunit/build
            
transform:
    [java] SQLUnit Transform Tool
    [java] Copyright(c) 2005, The SQLUnit Team

BUILD SUCCESSFUL
Total time: 5 seconds

The various files that drove the development of this tool can be found here:


SQLUnit TestDocsGenerator Tool

This tool was contributed by James Henderson.

It generates a listing of all available tests suites by traversing the supplied directory recursively, and a listing of all tests within each test suite. It is useful to determine if a test has been created to address specific criteria when testing your SQL code (or if a new test must be developed).

The tool assumes that your tests are organized hierarchically with each directory representing a test suite, and SQLUnit test file within each directory representing a test case. Each SQLUnit test file can have multiple tests.

This tool, when supplied with a directory, will recurse and process each SQLUnit XML file to extract the test name/description for each test within the test case. This process is performed for all SQLUnit XML files that are located by the recursion.

To run this tool, you can use the following ant command:


  $ ant test-doc [-Doutput.dir=/path/to/output/directory] [-Dtest.dir=/path/to/test/directory]
  
  
Buildfile: build.xml
 
init:
 
precompile:
 
compile:
 
test-doc:
   [delete] Deleting directory /home/sujit/src/sqlunit/output
    [mkdir] Created dir: /home/sujit/src/sqlunit/output
     [java] Processing /home/sujit/src/sqlunit/test/sybase/MyTestProcTest.xml:MyTestProcTest
     [java] Processing /home/sujit/src/sqlunit/test/mssqlserver/TestSQLUnit_SQLServer2KTypes.xml:TestSQLUnit_SQLServer2KTypes
     ...
     [java] Processing /home/sujit/src/sqlunit/test/mock/variabletests.xml:variabletests
     [java] Number of test cases processed:39
     [echo] XHTML TOC file(s) in output/Test*.html
  

The -Doutput.dir argument is optional, if not supplied, it will default to using the directory output/ under the current directory. The tool will generate three XHTML files that form the directory of all tests within the processed test suite.

The -Dtest.dir argument is also optional, if not supplied, it will process all XML files (with the .xml suffix) under the test/ subdirectory.

  • TestDirectory.html - a list of all processed test cases in alphabetical order and a sequential list of all tests within a test case. Each test case also has a link to its SQLUnit XML source file.

  • TestCaseIndex.html - an alphabetical index of all processed test cases. This file provides a link to the corresponding test case with its test listing in TestDirectory.html

  • TestCaseFrameset.html - creates a frameset document to house the previous two documents.

Most people will load TestCaseFrameset.html within a browser to get a complete view of all processed test cases and the tests within each test case.

Warning

This tool depends on certain naming conventions in the SQLUnit test suite as outlined below.


TestCaseName.xml
    TestCaseName[_1]: Description
    TestCaseName[_2]: Description
    ...
    TestCaseName[_3]: Description
      

If numbered test cases exist, the test listed in the output will be numbered. The test case name will be extracted from the first numbered test. The description will always follow the colon (:).

As an example:


MyTestCase.xml
    MyTestCase_1: A simple test
    MyTestCase_2: Another simple test
      

will have a test case name of MyTestCase (which matches the file name). Each test will be numbered and each description will be listing withi each test. The TestDirectory.html file when viewed through a browser will look something like this:


Tests within Test Case MyTestCase
1. A simple test
2. Another simple test
      

If the naming convention is not used, the entire name attribute of the first test is used as the name of the test case. Numbering of tests is not required, but useful. A test description which follows the colon is required.

Also, you cannot use the resource form of the DTD SYSTEM declaration in your SQLUnit test files, you must use either the relative or absolute form instead.

Some of these conventions may be relaxed or removed in the future with changes to the sqlunit.dtd to accomodate the requirements of this tool. Please contact James Henderson on the SQLUnit "Open Discussion" forum to discuss and suggest ideas/features or to report bugs about this tool.


SQLUnit Canoo2HTML Tool

This tool was contributed by James Henderson.

This tool converts the XML file generated by running SQLUnit with the Canoo reporter, into an XHTML document that can be displayed on a browser. This tool can be combined with the TestDocsGenerator to produce automated SQLUnit test reports, possibly with a continuous integration tool such as CruiseControl.

To run this tool, you can run this ant target:


  $ ant -Dlog.format=canoo \
  -Doutput.file=output/mytests.xml -Dtestfile=test/mock/coretests.xml \
  sqlunit-flat canoo2html
  
Buildfile: build.xml
 
init:
 
precompile:
 
compile:
    [javac] Compiling 10 source files to /home/sujit/src/sqlunit/build
 
compile-test:
    [javac] Compiling 31 source files to /home/sujit/src/sqlunit/build
 
def:
 
test:
 
init:
 
precompile:
 
compile:
    [javac] Compiling 10 source files to /home/sujit/src/sqlunit/build
 
canoo2html:
     [xslt] Processing /home/sujit/src/sqlunit/output/mytests.xml to /home/sujit/src/sqlunit/output/mytests.xml.html
     [xslt] Loading stylesheet /home/sujit/src/sqlunit/etc/canoo2html_result_transform.xsl
 
BUILD SUCCESSFUL
Total time: 10 seconds
  

The first target will run SQLUnit against the test/mock/coretests.xml file, and generate a Canoo XML test log in the output directory. The target need not necessarily be sqlunit-flat, it could also be sqlunit-nested or test from the distribution, or your own custom target to run the tests.

The second target applies an XSL transformation to the Canoo XML file to produce an XHTML file in the output directory (the same directory as the Canoo XML file). The XHTML file name is the same as the Canoo XML file name suffixed with .html. Thus, in our case described above, the XHTML file is output/mytests.xml.html.

The tests are color coded in the XHTML file, successful runs are shown in green and failures in red.


SQLUnit Tags


List of currently supported tags

The XML Tags that make up a SQLUnit test specification are described below.


The sqlunit Tag


Description

The root tag for an SQLUnit test specification.


Syntax


sqlunit ::= ((connection)+, (setup)*, (test | batchtest | diff | echo | func)+, (teardown)*)
      


Parent Elements

None. This is the top level element.


Nested Elements

Table 1. Nested Elements for sqlunit

NameDescriptionRequired
connection Specifies one or more database Connections that need to be instantiated for the test to run. Yes, one or more.
setup Specifies any setup tasks that need to happen before all the tests in the suite are run. No
test Specifies a test that needs to run in this suite. Either one or more of test, batchtest, diff, echo or func
batchtest Specifies a test that uses the JDBC batching functionality. Either one or more of test, batchtest, diff, echo or func
diff Specifies a test that compares the results generated by two SQL or stored procedure calls Either one or more of test, batchtest, diff, echo or func
echo Echoes a string after substitution to the log. Either one or more of test, batchtest, diff, echo or func
func Populates a single value returned from a stored procedure or SQL call into the SymbolTable, identified by function name Either one or more of test, batchtest, diff, echo or func
teardown Specifies any tasks that need to happen after all the tests in the suite are run No


The setup Tag


Description

The setup tag specifies operations that need to be carried out once before all the tests defined in the XML file. Typical tasks may include dropping and recreating the test database and tables, or deleting and inserting test data into the tables. A setup operation can be specified either by zero or more nested sql tags or by zero or more nested include tag which specify an external file containing the setup SQL to execute. It can also contain set tags to setup variables to be used later for the test, or foreach tags which can be used to execute a single SQL multiple times, such as insert SQL statements.


Syntax


setup ::= ((set)*, (((sql)*, (foreach)*) | (include)*))
      


Nested Elements

Table 2. Nested Elements for setup

NameDescriptionRequired
set Sets initial values for SQLUnit variables. No
funcdef Defines a SQL or stored procedure call that can be used like a function call (single string result). No
subdef Defines the structure of a SQL or stored procedure call. Values of parameters can be supplied or overriden in the actual sub call. No
sql Specifies the SQL statement to be executed with or without replaceable parameters. No
foreach Contains bulk SQL statements, such as inserts which are executed within the foreach tag. No
include Specifies an external file which contains the SQL statements to execute during the setup phase. No


Examples

A simple setup to delete rows from a table


 <setup>
   <sql><stmt>delete from foo where 1=1</stmt></sql>
 </setup> 
      


The skip Tag


Description

The skip tag is used to indicate to SQLUnit that this test should be skipped.


Syntax


skip ::= (BODY)
      


Attributes

Table 3. Attributes for skip

NameDescriptionRequired
value If set to true, then the test will be skipped No, default is false


The struct Tag


Description

The struct tag is used to describe a user-defined data type (UDT) object returned as a STRUCT type. The UDT and Java Class must be specified in a typedef tag. The returned object is treated as a JavaBean, and the fields are the bean properties. Contained fields MUST be given in alphabetical order by name, case-insensitive.


Syntax


struct ::= (field)*
      


Attributes

Table 4. Attributes for struct

NameDescriptionRequired
name name of the field (bean property) Yes


Nested Elements

Table 5. Nested Elements for struct

NameDescriptionRequired
field A field (bean property) of the returned object. Zero or more


Examples

Returning UDTs as STRUCTs.


 <outparam id="1" type="STRUCT">
   <struct>
     <field name="description">Red rubber ball</field>
     <field name="size">30</field>
   </struct>
 </outparam> 
      


The subparam Tag


Description

The param tag is used to specify an argument to a SQL statement or stored procedure, or aggregated into a set of paramset tags for batch calls. The body of the tag contains the value of the parameter.


Syntax


subparam ::= (BODY)
      


Attributes

Table 6. Attributes for subparam

NameDescriptionRequired
id Specifies the sequence number of the parameter to be replaced. The sequence is one-based to keep parity with the JDBC specification. Yes
name Specifies the name of the parameter to be replaced. This has been added for readability to help stored procedure authors/testers to spot errors quickly. No
type Specifies the type of the parameter to be replaced. The type is a String, and has a one to one mapping to the datatypes defined in java.sql.Types. It also allows some additional Oracle specific types specified in net.sourceforge.sqlunit.OracleExtensionTypes. The value of the type is the same as the field name of the member variables in Types or OracleExtensionTypes classes. Thus if the type is Integer, the corresponding SQL type is java.sql.Type.INTEGER, and the string value that should be used here is INTEGER. Yes
is-null If set to true, indicates that this parameter is an SQL NULL value. Valid values are true or false. No, default is false.
inout Specifies whether the parameter is an IN, OUT or INOUT parameter. Valid values are in, out and inout respectively. No, default is in.


Examples

A simple param tag definition


 <param id="1" name="v1" type="INTEGER">1</param>
 
      


The batchresult Tag


Description

The batchresult element specifies the expected result from running either the statements specified in a batchsql or batchcall tag


Syntax


batchresult ::= ((updatecount)*)
      


Attributes

Table 7. Attributes for batchresult

NameDescriptionRequired
expected-count Specifies the number of rows expected to be updated as a result of executing the batchsql or batchcall element. If the expected-count is specified, then the number of updatecount elements will not be counted for determining the test's success No
failed-at Specifies which statement in the batch is expected to fail when running the test. The index is zero-based No


Nested Elements

Table 8. Nested Elements for batchresult

NameDescriptionRequired
updatecount Specifies in the body of the element the number of rows expected to be modified by the SQL at the corresponding position (zero-based) in the batch Yes, unless either or both expected-count and failed-at are specified


Examples

A typical batchresult element


 <batchresult>
   <updatecount>1</updatecount>
   <updatecount>1</updatecount>
 </batchresult>
 
      


The call Tag


Description

The call tag is used to describe a call to a stored procedure or function. A stored procedure differs from a standard SQL call in that it can return multiple result sets in a single result. Not all databases support this functionality in the same way. Stored procedures are called within Java code using the standard call syntax that looks like {call [?=]stored_procedure_name([?,...])}. This is also the way it should be specified to SQLUnit. SQLUnit uses the CallableStatement to execute this call


Syntax


call ::= (stmt, (param)*)
      


Attributes

Table 9. Attributes for call

NameDescriptionRequired
id An internal id for the stored procedure call No
connection-id Used to specify a particular connection when multiple Connections are specified in a given test. If not specified, SQLUnit will try to use the default Connection, if one exists No


Nested Elements

Table 10. Nested Elements for call

NameDescriptionRequired
stmt Specifies the actual stored procedure call in JDBC format described above in the body of this element Yes
param A param element specifies a single replaceable parameter specified for the stored procedure call. Zero or more


Examples

Call to Stored Procedure dept_id=AddDept(dept_name)


 <call>
   <stmt>{? = call AddDept(?)}</stmt>
   <param id="1" type="INTEGER" inout="out">${deptId}</param>
   <param id="2" type="VARCHAR">Information Technology</param>
 </call> 
      


The set Tag


Description

The set tag allows declarations of variable names and associating them with values within the context of a single SQLUnit run. SQLUnit stores these variables and their values in an internal symbol table. Variables need not be pre-declared in order to be used. A variable is usually created in the symbol table by being assigned the appropriate value based on context, the first time it is referenced in the XML file. Subsequent references to the variable will return the value of the variable.


Syntax


set ::= ((sql|call),result)?|(constructoryArgs*,methodArgs*)?
      


Attributes

Table 11. Attributes for set

NameDescriptionRequired
name Specifies the name of the SQLUnit variable. A variable must always be specified like ${varname}. Attempts to specify variables like varname or $varname will result in syntax error. Yes
value Specifies the initial value of the variable. The value must be specified. In cases where you do not know the value, you can initialize it with a default value, or not invoke the set tag to declare the variable. When the variable is first encountered in SQLUnit, it will implicitly create an entry for that variable in the symbol table. Not required if nested elements are specified.
static Specifies whether the method call is static. Valid values are true and false. Required if the class and method attributes are also supplied
class Specifies the class name whose method has to be invoked to populate the variable Only required if the method attribute is also supplied
method Specifies the method name in the specified class to invoke. Only required if the class attribute is supplied


Nested Elements

Table 12. Nested Elements for set

NameDescriptionRequired
sql Specifies a SQL statement to invoke which will populate the variables in the result. Only required if there is no value attribute in the set tag. A call tag can also be specified here instead.
call Specifies a stored procedure call to invoke which will populate the variables in the result. Only required if there is no value attribute in the set tag. A sql tag can also be specified instead.
result Specifies a result tag with variables in the col tag body. The variables will be populated from the values returned by the SQL or stored procedure call. Only required if there is either a sql or a call tag.
constructorArgs Specifies arguments to a constructor of a non-static method if that is being used to populate the variable to set. Only required if we want to invoke a non-static method
methodArgs Specifies arguments to the method to be executed to populate the variable to be set Required if we want to invoke a method to set the variable


Examples

Setting a variable ${var} to 14


 <set name="${var}" value="14" /> 
      

Setting variable ${myquery.col1} from SQL


 <set name="${myquery}">
   <sql><stmt>select col1 from mytable where col2=45</stmt></sql>
   <result>
     <resultset id="1">
       <row id="1">
         <col id="1" name="c1" type="INTEGER">${col1}</col>
       </row>
     </resultset>
   </result>
 </set> 
      

Setting variable ${var} from a method call return


 <set name="${var}" class="java.lang.System" static="true"
     method="getProperty">
   <methodArgs>
     <arg name="key" type="java.lang.String"
     value="property" />
   </methodArgs>
 </set> 
      


The classifiers Tag


Description

Provides the caller with the ability to classify and/or group tests by a given criteria. When the test is executed, the caller can pass to it some properties and the handler decides whether the test matches them. If the test matches it is executed, and if not, it is skipped and reported as skipped.


Syntax


classifiers ::= ((severity)?, (category)?)
      


Examples

An example of a classifiers tag


 <test name="Find Agent by Id test"
     failure-message="Find Agent by Id test failed">
   <classifiers>
     <severity>INFO</severity>
     <category>findById</category>
   </classifiers>
   <sql connection-id="mysql">
     ...
   </sql>
 </test> 
      


The jndi Tag


Description

A wrapper tag to group together JNDI properties that are needed to instantiate an InitialContext for the JNDI DataSource lookup.


Syntax


jndi ::= ((arg)*)
      


Nested Elements

Table 13. Nested Elements for jndi

NameDescriptionRequired
arg A tag that specifies the name and value of a single JNDI property. One or more arg elements are required.


Examples

A typical JNDI tag for getting a DataSource from JBoss


 <connection>
   <datasource>jdbc/myDSN</datasource>
   <jndi>
     <arg name="java.naming.factory.initial"
       value="org.jnp.interfaces.NamingContextFactory" />
     <arg name="java.naming.provider.url" 
       value="jnlp://localhost:1099" />
     <arg name="java.naming.factory.url.pkgs" 
       value="org.jboss.naming" />
   </jndi>
 </connection>
      


The methodinvoker Tag


Description

The methodinvoker tag allows the caller to invoke methods in Java code and check to see if they complete without exception. These methods are generally, but need not be, JDBC specific. Any objects that are returned from the methods are ignored. The tag only verifies that the method ran successfully, and if not, it threw an expected exception.


Syntax


methodinvoker ::= ((constructorArgs)*, (methodArgs)*)
      


Attributes

Table 14. Attributes for methodinvoker

NameDescriptionRequired
static If set to true, specifies that the method being invoked is static. Valid values are true and false. No, default is false.
class Specifies the full class name of the class in which the method is defined. Yes
method Specifies the method name to invoke. Yes


Nested Elements

Table 15. Nested Elements for methodinvoker

NameDescriptionRequired
constructorArgs Wraps a set of arg elements which specify the arguments, if any, that the constructor will need. Not required for static methods, or if the class has a default no-args constructor.
methodArgs Wraps a set of arg elements which specify the arguments, if any, that the method will need. Not required if the method takes no arguments.


Examples

A simple methodinvoker tag without method arguments


 <methodinvoker class="net.sourceforge.sqlunit.utils.TestArgument"
     method="toString" >
   <constructorArgs>
     <arg name="c7" type="java.lang.Double.TYPE" value="0.2" />
   </constructorArgs>
 </methodinvoker> 
      


The teardown Tag


Description

The teardown tag specifies SQL operations that need to be carried out after all the tests in the XML file are done. If SQLUnit detects a test failure, it will skip the remaining tests and invoke the teardown operations before exiting. Typical tasks include dropping the test database or deleting data from test tables.


Syntax


teardown ::= (((sql)*, (foreach)*) | (include)*)
      


Nested Elements

Table 16. Nested Elements for teardown

NameDescriptionRequired
sql Specifies the SQL statements to be executed, with or without replaceable parameters, that should executed on teardown. No
foreach Contains bulk SQL statements such as SQL DELETE statements, which will be executed from within a foreach tag. No
include Specifies an external file which contains the SQL statements to be executed on teardown. No


Examples

Dropping some test tables on teardown


 <teardown>
   <sql><stmt>drop table testdata</stmt></sql>
   <sql><stmt>drop table testdata2</stmt></sql>
 </teardown> 
      


The foreach Tag


Description

The foreach tag allows for simple bulk substitution by replacing a named parameter in a SQL statement with values from a supplied list or values generated by incrementing a counter. The syntax is similar to FOR and FOREACH statements found in most programming languages.


Syntax


foreach ::= ((sql)*)
      


Attributes

Table 17. Attributes for foreach

NameDescriptionRequired
param The name of the parameter that will be substituted in the SQL statement. For example, if the param is id, then all occurences of ${id} will be substituted by the current value of id. Yes
values A comma-separated list of values for the id. A value set can be specified within each element in the list. The elements of the set are separated with semi-colons. They are referred to later in the scope of the foreach tag as value_of_param.index. The index is 0-based. No, either this or (start,stop,(step)) or (start,count,(step)) may be specified
start Specifies the starting value of the sequence of values for the param No, values can be specified instead. If neither is specified, then start defaults to 0.
stop The ending value of the sequence of values for param No, count can be specified instead.
step The increment if a sequence is specified with start and stop No, defaults to 1.
count The number of entries in the sequence specified by start and stop. No, stop can be specified instead.


Nested Elements

Table 18. Nested Elements for foreach

NameDescriptionRequired
sql Contains a single SQL statement with placeholders for param which will be executed in a loop specified by the foreach attributes. The replaceable placeholder(s) should be specified as ${id}. Yes


Examples

A foreach used to prepare test data


 <prepare>
   <sql><stmt>delete from foreachtest where 1=1</stmt></sql>
   <foreach param="id" start="0" stop="10" step="1">
     <sql>       <stmt>insert into foreachtest (id,name) values (${id},'name${id}')</stmt>
     </sql>
   </foreach>
 </prepare>
 
      


The connection Tag


Description

Supplies information for the SQLUnit test harness to connect to the underlying database containing the stored procedures to be tested. Connections can be built using JDBC or from a DataSource object looked up through JNDI. Connections can also be specified using file URLs. Multiple connection tags can be specified if more than one Connection is needed for a single SQLUnit test suite.


Syntax


connection ::= (((driver, url, user, password, (jarfile-url)?) | (datasource, jndi))?)
      


Attributes

Table 19. Attributes for connection

NameDescriptionRequired
connection-id When multiple Connections are defined in a SQLUnit test, this attribute refers to a Connection to use for running this call by its connection-id. If not specified, SQLUnit will try to look up the default Connection which has no connection-id attribute defined. No, default Connection is used if not supplied
extern If specified, the value of this attribute points to a Java properties file which contains the information necessary to build the Connection. The file can be specified without path information if it can be found in the caller's CLASSPATH or specified as a relative path, as well as an absolute path on the file system. A sample properties file can be found in /cvs/test/postgresql/sqlunit.properties. No
transaction-support Indicates whether transaction support is provided by SQLUnit. Setting this to on (the default) will make SQLUnit treat each SQL or CALL as a single unit of work. Turning this to off will make SQLUnit not do any COMMITs or ROLLBACKs, it is left to the client to enforce boundaries of the unit of work by putting COMMIT and ROLLBACK in the SQL or stored procedures. Finally, turning this to implicit will put SQLUnit in auto commit mode, but SQLUnit will not do any explicit COMMIT or ROLLBACK. Transaction boundaries will either by enforced by the database if it supports it, or by the JDBC autocommit mechanism if not. No, defaults to on
reconnect-on-failure Will destroy the current Connection and rebuild it in the event of a test failure or exception. This is needed to work around buggy database drivers which leave the Connection in an inconsistent state in such cases. No, defaults to false
server-name If specified, will override the database server name that is derived from the DatabaseMetaData for the connection. This is used to lookup data types supported by this database. No, defaults to the database server name


Nested Elements

Table 20. Nested Elements for connection

NameDescriptionRequired
driver Specifies the full Java class name for the JDBC driver. This is needed for building the Connection using JDBC. Yes (JDBC)
url Specifies the JDBC URL to the database Yes (JDBC)
user Specifies the database user who will connect to the database Yes (JDBC)
password Specifies the password for the database user Yes (JDBC)
jarfile-url Specifies a URL (eg. file:///tmp/mydriver.jar) for a JAR file containing a JDBC driver which is not in the CLASSPATH. No (JDBC)
datasource Specifies the name of a DataSource. By convention, this would look something like jdbc:/myDSN, where the DataSource object is actually stored under java:/comp/env/jdbc. This is likely to vary between JNDI servers and sites, so check with your administrator for the DataSource name to use Yes (JNDI)
jndi Empty tag, contains zero or more arg elements containing the name-value pairs to pass in when creating the Initial Naming Context that will be used to look up the DataSource object. These name-value pairs locate the JNDI service. Each arg element will correspond to a line in the jndi.properties file for the given JNDI server. Yes (JNDI)


Examples

Specifying a JDBC connection for PostgreSQL


 <connection>
   <driver>org.postgresql.Driver</driver>
   <url>jdbc:postgresql://localhost:5432/demodb</url>
   <user>defaultuser</user>
   <password>d3fau1tus3r</password>
 </connection>
      

Specifying a JNDI connection with JBoss


 <connection>
   <datasource>jdbc/myDSN</datasource>
   <jndi>
     <arg name="java.naming.factory.initial"
       value="org.jnp.interfaces.NamingContextFactory" />
     <arg name="java.naming.provider.url" 
       value="jnlp://localhost:1099" />
     <arg name="java.naming.factory.url.pkgs" 
       value="org.jboss.naming" />
   </jndi>
 </connection>
      


The func Tag


Description

The func tag will replace the positional parameters in the SQL string defined in the corresponding funcdef tag, then run it and return a result.


Syntax


func ::= (EMPTY)
      


Attributes

Table 21. Attributes for func

NameDescriptionRequired
name The name of the func instance. The value returned from the execution of the func element will be available in the SymbolTable as ${func.name_of_func} for later tests in the same suite. Yes
lookup The name of the funcdef element to look up. Yes
connection-id The id of the connection to use, when multiple connections are defined. If not specified, it will use the default connection. No
param0-9 The value for ${0}-${9} in the SQL string, if it exists. Note that if the variable is a string, you must supply the enclosing single quotes to indicate that. This type of query has no indication of the type of parameter being passed to it. No
id The name or number of the sql call No


Nested Elements

Table 22. Nested Elements for func

NameDescriptionRequired
skip Indicates whether the func should be skipped or not. No
classifiers Allows user to provide classification criteria for the func which SQLUnit can use to decide whether it should run the func or not based on criteria provided to match the classifier. No


Examples

Calling a definition without parameters


<func name="databasename" lookup="databasedef" />
      

Calling a custom function


<func name="mycol" lookup="customdef" param="234" />
      


The echo Tag


Description

Echoes the specified value to the log. This is mainly used for debugging a test.


Syntax


echo ::= EMPTY
      


Attributes

Table 23. Attributes for echo

NameDescriptionRequired
name A name for this echo tag Yes, required by the SQLUnit logger
text The text to echo Yes
value Specifies the expected value of text, if used in a test context. Saves caller the trouble of having to manually compare the expected and actual returned values. no


The include Tag


Description

The include tag is used to specify an external file that contains a list of SQL statements that should be executed together. The statements can be separated from one another by a free standing slash (/) or semi-colon character (;) on its own line, or by a line terminated by a semi-colon (;) character.


Syntax


include ::= (EMPTY)
      


Attributes

Table 24. Attributes for include

NameDescriptionRequired
file Specifies a relative or absolute path name to the file containing the SQL statements. Yes
connection-id When multiple Connections are defined in a SQLUnit test, this attribute is used to select the Connection to use for running the SQL statements. If not specified, SQLUnit will use the default Connection if that is specified. No


Examples

Cleanup SQL being included in a teardown tag


 <teardown>
   <include file="test/sybase/teardown.sql" />
 </teardown> 
      


The result Tag


Description

The result tag allows the test author to specify an expected result from a SQL call or stored procedure. SQLUnit executes the SQL or stored procedure and converts the results into an internal Result object. It will also convert the data supplied in the result tag to an internal Result object, and then compares the two for equality. Values specified in the col nested element of a result element can contain variable names instead of literal values. The variable names will be updated with the values from the internal symbol table. Result tags can also contain an exception element if the call is expected to throw an exception for this particular test. Note that all id attributes of the result and its nested elements start at 1, so as to be consistent with how JDBC resultsets are set up, and minimize confusion between the two representations.


Syntax


result ::= ((((outparam)*, (updatecount)?, (resultset)*)+ | ((outparam)*, exception))?)
      


Attributes

Table 25. Attributes for result

NameDescriptionRequired
id The id of the result No
echo If set to true, the result will be echoed to the output. The test will be short-circuited and SQLUnit will not attempt to do any matching. This is useful for debugging. Valid values are true and false. No, defaults to false


Nested Elements

Table 26. Nested Elements for result

NameDescriptionRequired
outparam Zero or more outparam elements may be specified, each representing an output parameter from the stored procedure. An outparam element can contain a resultset element in case of Oracle CURSOR types, or text information in case of non-CURSOR types. No
updatecount Specifies the updatecount that is returned from the SQL or stored procedure call. No.
resultset The resultset tag specifies a single resultset returned from a database call (SQL or stored procedure). A result element can contain one or more resultsets, which in turn will contain one or more rows, which would contain one or more columns. If there are no rows, a resultset can also be empty. No.
exception The exception tag is used to test for expected failures of a stored procedure in response to specific inputs. No. Exception tags can coexist with outparam tags.


Examples

A multi-resultset result tag


 <result>
   <resultset id="1">
     <row id="1">
       <col id="1" name="c1" type="INTEGER">1</col>
       <col id="2" name="c2" type="VARCHAR">Blah</col>
       <col id="3" name="c3" type="INTEGER">3</col>
     </row>
   </resultset>
   <resultset id="2" />
   <resultset id="3" />
   <resultset id="4">
   <row id="1">
       <col id="1" name="ac1" type="INTEGER">1</col>
       <col id="2" name="ac2" type="INTEGER">1</col>
       <col id="3" name="ac3" type="VARCHAR">Unused Blahs</col>
       <col id="4" name="ac4" type="INTEGER">1</col>
       <col id="5" name="ac5" type="VARCHAR">Unused</col>
   </row>
   <row id="2">
       <col id="1" name="ac1" type="INTEGER">1</col>
       <col id="2" name="ac2" type="INTEGER">1000</col>
       <col id="3" name="ac3" type="VARCHAR">Deprecated Blahs</col>
       <col id="4" name="ac4" type="INTEGER">1</col>
       <col id="5" name="ac5" type="VARCHAR">Deprecated</col>
   </row>
   </resultset>
 </result> 
      

A result containing an exception


 <result>
   <exception>
     <code>1234</code>
     <message>Test Exception</message>
   </exception>
 </result> 
      


The methodArgs Tag


Description

A wrapper tag to group together arguments to a method call.


Syntax


methodArgs ::= ((arg)*)
      


Nested Elements

Table 27. Nested Elements for methodArgs

NameDescriptionRequired
arg A tag that specifies the name, type and value of the method argument. One or more arg elements are required.


Examples

A methodArgs tag with different argument types


 <methodArgs>
   <arg name="m3" type="java.lang.Integer" value="-2147483648" />
   <arg name="m7" type="java.lang.Double" value="2.2" />
   <arg name="m8" type="java.lang.String" value="Some text" />
 </methodArgs> 
      


The row Tag


Description

Represents a row in a database table returned from a SQL or stored procedure call.


Syntax


row ::= (col)+
      


Attributes

Table 28. Attributes for row

NameDescriptionRequired
id The row id Yes
partial If true, indicates that child cols are partially specified. Valid values are true and false. Columns will be matched based on the col element's id attribute. No, defaults to false


Nested Elements

Table 29. Nested Elements for row

NameDescriptionRequired
col A row can have one or more col elements defined within it. Yes


Examples

A simple row tag


 <row id="1">
   <col id="1" name="col1" type="INTEGER">1</col>
 </row> 
      


The param Tag


Description

The param tag is used to specify an argument to a SQL statement or stored procedure, or aggregated into a set of paramset tags for batch calls. The body of the tag contains the value of the parameter.


Syntax


param ::= (BODY)
      


Attributes

Table 30. Attributes for param

NameDescriptionRequired
id Specifies the sequence number of the parameter to be replaced. The sequence is one-based to keep parity with the JDBC specification. Yes
name Specifies the name of the parameter to be replaced. This has been added for readability to help stored procedure authors/testers to spot errors quickly. No
type Specifies the type of the parameter to be replaced. The type is a String, and has a one to one mapping to the datatypes defined in java.sql.Types. It also allows some additional Oracle specific types specified in net.sourceforge.sqlunit.OracleExtensionTypes. The value of the type is the same as the field name of the member variables in Types or OracleExtensionTypes classes. Thus if the type is Integer, the corresponding SQL type is java.sql.Type.INTEGER, and the string value that should be used here is INTEGER. Yes
scale Specifies the scale of the parameter value if the value maps to a BigDecimalType. This is an optional parameter. SQLUnit will try its best to guess the scale based on the actual value, but this is useful if the value cannot be specified or is NULL. No
typename Specifies the name of a user-defined type. This is an optional parameter. No
is-null If set to true, indicates that this parameter is an SQL NULL value. Valid values are true or false. No, default is false.
inout Specifies whether the parameter is an IN, OUT or INOUT parameter. Valid values are in, out and inout respectively. No, default is in.


Examples

A simple param tag definition


 <param id="1" name="v1" type="INTEGER">1</param>
 
      


The funcdef Tag


Syntax


funcdef ::= 
      


Attributes

Table 31. Attributes for funcdef

NameDescriptionRequired
name The name of the function definition. This will be used to call the definition from the func tag. Yes
query The SQL query with numbered replaceable parameters (see example below) which will be called to return the result. Yes
description User Documentation for the function definition. No


Examples

Defining call to MySQL built-in function


 <funcdef name="databasedef" query="select DATABASE()"
     description="Returns the current database name" /> 
      

Defining call to a custom function


 <funcdef name="customdef"
   query="select myCol from myTable where myCol1 = ${0}"
   description="Defines a custom function" /> 
      


The batchsql Tag


Description

The batchsql tag allows the client to specify a set of non-parameterized SQL statements which need to run in a batch. SQLUnit uses the JDBC batching mechanism to run the set of SQL statements.


Syntax


batchsql ::= ((stmt)+)
      


Attributes

Table 32. Attributes for batchsql

NameDescriptionRequired
connection-id When multiple Connections are defined in a SQLUnit test, this attribute provides a mechanism for choosing one of the defined Connections to use for running this batch. If a connection-id is not specified, SQLUnit will try to look up a Connection which has no connection-id. No


Nested Elements

Table 33. Nested Elements for batchsql

NameDescriptionRequired
stmt Contains the SQL statement or stored procedure call in the body of the element One or more stmt elements need to be specified


Examples

A typical batchsql call


 <batchsql>
   <stmt>delete from customer where custId=1</stmt>
   <stmt>insert into customer values(1,'Some One','secret',
     'en_US',null,0,now())</stmt>
   <stmt>insert into customer values(1,'Someone Else','secret',
     'en_US',null,0,now())</stmt>
   <stmt>delete from customer where 1=1</stmt>
 </batchsql> 
      


The prepare Tag


Description

The prepare tag specifies additional SQL statements that should be run before the test in whose block it appears in. This is used for specifying additional operations that need to happen on a per-test basis, which is not covered in the setup tag.


Syntax


prepare ::= ((set)*, (sql)*, (foreach)*)
      


Nested Elements

Table 34. Nested Elements for prepare

NameDescriptionRequired
set This tag sets values for SQLUnit test variables into the SQLUnit symbol table. No.
sql Identifies one or more SQL statements that should be executed as part of the per-test prepare No.
foreach Specifies a block within a foreach block which is executed as part of the prepare. No.


Examples

A foreach tag inside a prepare tag


 <prepare>
   <sql><stmt>delete from foreachtest where 1=1</stmt></sql>
   <foreach param="id" start="0" stop="10" step="1">
     <sql><stmt>insert into foreachtest (id,name) values 
       (${id},'name${id}')</stmt></sql>
   </foreach>
 </prepare> 
      


The typedef Tag


Description

The typedef tag defines a UTD type name and corresponding Java class name.


Syntax


typedef ::= (EMPTY)
      


Attributes

Table 35. Attributes for typedef

NameDescriptionRequired
typename name of the UDT as defined in the database Yes
classname name of the fully-qualified Java class Yes


Examples

Defining UTD-Java class mappings.


 <typemap>
     <typedef typename="MY_SQL_OBJ"
       classname="org.my.stuff.MyClass" />
     <typedef typename="THEIR_SQL_OBJ"
       classname="org.their.things.TheirClass" />
 </typemap>
 
      


The severity Tag


Description

Allows caller to define various levels of Severity for a test. Valid values are FATAL, ERROR, WARN, INFO and DEBUG.


Syntax


severity ::= (BODY)
      


The paramset Tag


Description

Wrapper tag for one or more param elements.


Syntax


paramset ::= ((param)+)
      


Attributes

Table 36. Attributes for paramset

NameDescriptionRequired
id Specifies the id of the paramset element. Yes


Nested Elements

Table 37. Nested Elements for paramset

NameDescriptionRequired
param Specifies argument names and values to be passed into a batchcall call. One or more


Examples

A paramset passed into an INSERT batchcall


 <paramset id="1">
   <param id="1" type="INTEGER">1</param>
   <param id="2" type="VARCHAR">Some One</param>
 </paramset> 
      


The subdef Tag


Description

The subdef tag is used to define a stored procedure or SQL call. All parameters need to be defined, but some parameters may not have values associated with them. These are plugged in when the actual call is made using the sub tag. This allows test writers to save keystrokes and work at a higher level of abstraction than building the tests manually with stored procedures or SQL calls each time.


Syntax


subdef ::= ((param)*)
      


Attributes

Table 38. Attributes for subdef

NameDescriptionRequired
name The name of the subroutine definition. This will be used by the sub tag to specify the definition to look up. Yes
query The SQL or stored procedure call with replaceable parameters specified as in the sql or call tags. Yes
description A user-friendly description of the subroutine definition. Used for documentation No
type Specifies whether to treat the SQL as a stored procedure or SQL call when called. Valid values are sql and call. No, defaults to sql


Nested Elements

Table 39. Nested Elements for subdef

NameDescriptionRequired
param Specifies zero or more parameters that are supplied with the subroutine definition. All parameters that the call requires should be supplied, although some or all the values may remain unspecified. Unspecified values are denoted with a single '?' character in the text of the param element. Zero or more parameters can be specified


Examples

A sample subroutine definition


 <subdef name="UpsertStreetDef"
   query="{? = pk_edit_street.upsert_street(?,?)}"
   description="Definition of Upsert Street">
     <param id="1" type="INTEGER" name="rc" inout="out">
 {t}    ${streetId}
     </param>
     <param id="2" type="VARCHAR" name="street">?</param>
     <param id="3" type="VARCHAR" name="city">SFO</param>
 </subdef> 
      


The col Tag


Description

Represents a column of a database table returned from a SQL or stored procedure call.


Syntax


col ::= (BODY)
      


Attributes

Table 40. Attributes for col

NameDescriptionRequired
id The column id Yes
name The column name No
type The XML name of the data type for the column Yes


Examples

A simple column tag


<col id="1" name="col1" type="INTEGER">1</col>
      


The batchtest Tag


Description

The batchtest tag allows specifying a batch of SQL statements or stored procedure calls that must be executed within a batch. It allows the client to specify the expected number of results, and a point where a failure may be expected. Note that only statements which update data in some way should be specified hre, since the JDBC specification does not allow the returning of resultsets from SQL statements run using the batching feature


Syntax


batchtest ::= ((skip)?, (classifiers)?, (batchcall | batchsql), batchresult)
      


Attributes

Table 41. Attributes for batchtest

NameDescriptionRequired
name The name of the batch test. THis is used by SQLUnit to print progress messages to its log output Yes
assert Specifies a single or comma-separated assertion that must hold true for the batch test. No, defaults to equal if not specified.
failure-message If specified, SQLUnit will print the user-supplied failure message if the test failed No, default is no failure message
java-object-support If set to true, Java Object Support is enabled for the test No, default is false


Nested Elements

Table 42. Nested Elements for batchtest

NameDescriptionRequired
skip Indicates whether the func should be skipped or not. No
classifiers Allows user to provide classification criteria for the func which SQLUnit can use to decide whether it should run the func or not based on criteria provided to match the classifier. No
batchcall Specifies the set of batch calls to be made. Either batchcall or batchsql must be specified No, but see Description
batchsql Specifies the set of batch calls to be made. Either batchcall or batchsql must be specified No, but see Description
batchresult Specifies the expected return values from the call Yes


Examples

A typical batchtest specification


 <batchtest name="Testing basic batchtest functionality">
   <batchsql>
     <stmt>delete from customer where custId=1</stmt>
     <stmt>insert into customer values(1,'Some One','secret',
       'en_US',null,0,now())</stmt>
     <stmt>insert into customer values(1,'Someone Else','secret',
       'en_US',null,0,now())</stmt>
     <stmt>delete from customer where 1=1</stmt>
   </batchsql>
   <batchresult>
     <updatecount>1</updatecount>
     <updatecount>1</updatecount>
     <updatecount>1</updatecount>
     <updatecount>2</updatecount>
   </batchresult>
 </batchtest> 
      


The constructorArgs Tag


Description

Collection of arg elements that need to be passed to the constructor of a specified class whose method needs to be invoked by SQLUnit


Syntax


constructorArgs ::= ((arg)*)
      


Nested Elements

Table 43. Nested Elements for constructorArgs

NameDescriptionRequired
arg Specifies an individual argument One or more


Examples

Example of a constructorArgs tag


 <constructorArgs>
   <arg name="c1" type="java.lang.Byte" value="-128" />
   <arg name="c2" type="java.lang.Short" value="-32768" />
   <arg name="c3" type="java.lang.Integer" value="-2147483648" />
   <arg name="c5" type="java.lang.Character" value="a" />
   <arg name="c6" type="java.lang.Float" value="1.1" />
   <arg name="c7" type="java.lang.Double" value="2.2" />
   <arg name="c8" type="java.lang.String" value="Some text" />
 </constructorArgs> 
      


The batchcall Tag


Description

The batchcall tag allows the SQLUnit client to run a single stored procedure or parameterized SQL with a set of different input arguments. It uses JDBC batching to do this.


Syntax


batchcall ::= ((stmt), (paramset)+)
      


Attributes

Table 44. Attributes for batchcall

NameDescriptionRequired
connection-id When multiple Connections are defined for a SQLUnit test, this attribute refers to the Connection to use for running this call. If not specified, SQLUnit will try to look up a Connection which has no connection-id attribute specified. No


Nested Elements

Table 45. Nested Elements for batchcall

NameDescriptionRequired
stmt Contains the SQL statement or stored procedure to call in the body. Yes
param Specifies a set of param elements At least one needs to be provided


Examples

A typical batchcall tag


 <batchcall>
   <stmt>insert into customer values(?,?,?,?,?,?,now())</stmt>
     <paramset id="1">
       <param id="1" type="INTEGER">1</param>
       <param id="2" type="VARCHAR">Some One</param>
       <param id="3" type="VARCHAR">secret</param>
       <param id="4" type="VARCHAR">en_US</param>
       <param id="5" type="VARCHAR">SQLUnit</param>
       <param id="6" type="INTEGER">0</param>
     </paramset>
     <paramset id="2">
       <param id="1" type="INTEGER">2</param>
       <param id="2" type="VARCHAR">Someone Else</param>
       <param id="3" type="VARCHAR">secret</param>
       <param id="4" type="VARCHAR">en_US</param>
       <param id="5" type="VARCHAR">SQLUnit</param>
       <param id="6" type="INTEGER">0</param>
     </paramset>
 </batchcall> 
      


The outparam Tag


Description

The outparam tag is used to represent OUT parameters returned as a result of a Stored procedure call. The value is stored in the body text as a String or as a embedded resultset element in case of CURSOR type OUT parameters.


Syntax


outparam ::= (BODY)|(resultset)|(struct)
      


Attributes

Table 46. Attributes for outparam

NameDescriptionRequired
id The param-id of the outparam parameter. Yes
name The name of the outparam parameter. This is mainly for readability to help stored procedure authors/testers to spot errors quickly. No
type Specifies the type name of the parameter. Yes


Nested Elements

Table 47. Nested Elements for outparam

NameDescriptionRequired
resultset This is needed only when the type of the OUT parameter is a CURSOR, and specifies the value of the CURSOR outparam. Not if the type is not CURSOR


Examples

A outparam with a INTEGER value


 <outparam id="1" type="INTEGER">24</outparam> 
      

A outparam tag with an embedded CURSOR


 <outparam id="1" type="oracle.CURSOR">
   <resultset id="1">
     <row id="1">
       <col id="1" type="INTEGER">7</col>
       <col id="2" type="VARCHAR">James Bond</col>
       <col id="3" type="VARCHAR">Martini</col>
     </row>
   </resultset>
 </outparam> 
      


The typemap Tag


Description

The typemap tag defines mappings of user-defined data types (UDTs) to their corresponding Java classes. These mappings are required for handling all STRUCT type values in the tests that use this connection. This includes nested UDT objects.


Syntax


typemap ::= (typedef)*
      


Nested Elements

Table 48. Nested Elements for typemap

NameDescriptionRequired
typedef A UTD type name and corresponding Java class name. Zero or more


Examples

Defining mappings from UTDs to Java classes.


 <connection>
   <typemap>
     <typedef typename="MY_SQL_OBJ"
       classname="org.my.stuff.MyClass" />
   </typemap>
 </connection>
 
      


The test Tag


Description

The test tag specifies the SQL statement or stored procedure that needs to be tested, its input parameters, and the expected output. The tag will be run by SQLUnit to compare the expected results against the actual result.


Syntax


test ::= ((skip)?, (classifiers)?, (match)*, (prepare)?, ((sql | call | methodinvoker | dynamicsql), result)?)
      


Attributes

Table 49. Attributes for test

NameDescriptionRequired
name Specifies a human-readable name for the test, which will be printed to the log as part of SQLUnit's progress messages. Yes
assert Specifies a single or comma-separated list of assertions that must be true for the test. No, defaults to equal
failure-message Allows the caller to supply an error message which should be displayed if the test failed. No, if not specified, no user message will be displayed.
java-object-support If set to on, indicates that Java Object Support is turned on. Valid values are on and off. No, default is off
expected-duration-millis If specified, specifies that SQLUnit should time the test and fail the test if the test does not complete in the window specified +/- a percentage tolerance if specified, or 10% if not. No
percentage-tolerance If specified with expected-duration-millis, this specifies the percentage tolerance that SQLUnit will put on the expected-duration in order to calculate if the test should fail. No, will default to 10 if expected-duration-millis is specified and percentage-tolerance is not specified.


Nested Elements

Table 50. Nested Elements for test

NameDescriptionRequired
skip Indicates whether the test should be skipped or not. No
classifiers Allows user to provide classification criteria for the test which SQLUnit can use to decide whether it should run the test or not based on criteria provided to match the classifier. No
match Specifies zero or match elements that should be applied to match the result returned with that specified. No
prepare Specifies SQL setup code that must be run on a per-test basis. No
sql Specifies the SQL statement that must be run for this test. Either one of sql, call, methodinvoker, dynamicsql or sub
call Specifies a stored procedure that must be run for the test. Either one of sql, call, methodinvoker, dynamicsql or sub
methodinvoker Specifies a method that should be invoked for the test. Either one of sql, call, methodinvoker, dynamicsql or sub
dynamicsql Specifies a method which returns a String of dynamic SQL code that should be executed for this test. Either one of sql, call, methodinvoker, dynamicsql or sub
sub Specifies a predefined and partially specified named SQL or stored procedure call Either one of sql, call, methodinvoker, dynamicsql or sub func is required.
result Specifies the expected result from the test. Yes


Examples


 <test name="Adding department HR">
   <sql>
     <stmt>select AddDept(?)</stmt>
     <param id="1" type="VARCHAR">Human Resources</param>
   </sql>
   <result>
     <resultset id="1">
       <row id="1">
         <col id="1" name="adddept" type="INTEGER">
           ${deptId_HR}
         </col>
       </row>
     </resultset>
   </result>
 </test> 
      


The resultset Tag


Description

Represents a resultset returned from a SQL or stored procedure call.


Syntax


resultset ::= (row)*
      


Attributes

Table 51. Attributes for resultset

NameDescriptionRequired
id The resultset id Yes
partial If set to true, indicates that rows are partially specified. Valid values are true and false. Rows will be matched based on the row's id attribute. No, defaults to false
rowcount Specifies the number of rows in the resultset. This is specified when the test client wants SQLUnit to only match the number of rows in the resultset, rather than the contents themselves. No
order-by A comma-separated list of column ids on which the rows in the resultset should be sorted. By default, both the resultset returned from the SQL or stored procedure call and the resultset that is specified will be sorted before comparing. The default sorting is by all the specified columns in the order in which they appear. The order-by attribute should be used only if the default sort needs to be overriden, for example, when the SQL or stored procedure returns ordered data. The sort order specified is ASCENDING, unless the column id is prefixed with a negative sign, which will cause it to be sorted in DESCENDING order. Setting order-by to NONE will turn off auto-sorting. No


Nested Elements

Table 52. Nested Elements for resultset

NameDescriptionRequired
row A resultset can have zero or more row elements No


Examples

A simple resultset tag containing a single row


 <resultset id="1">
   <row id="1">
     <col id="1" name="col1" type="INTEGER">1</col>
   </row>
 </resultset> 
      


The match Tag


Description

The match tag specifies a user-defined matching strategy that will be applied instead of the default strategy to check for exact equality between two column values


Syntax


match ::= ((arg)*)
      


Attributes

Table 53. Attributes for match

NameDescriptionRequired
resultset-id If specified, defines the resultset(s) on which the user- defined matching criteria should be applied. Wildcards may be applied to specify multiple values using a '*', eg. '3*' or '*'. Ranges may be specified using an expression such as '1-2', or enumerations using an expression of the form '1,2,3'. No, defaults to all resultsets if not specified
row-id If specified, defines the row(s) on which the user-defined matching strategy will be applied. Same wildcarding rules as defined in resultset-id apply. No, defaults to all if not specified
col-id If specified, defines the column(s) on which the user-defined matching strategy should be applied. Same wildcarding rules as defined in resultset-id apply. No, defaults to all if not specified
matcher The full class name of the Matcher class that will be used for the user-defined matching. The Matcher class must implement the IMatcher interface and be available in the caller's CLASSPATH Yes


Nested Elements

Table 54. Nested Elements for match

NameDescriptionRequired
arg Specifies the name and value of the parameters that are needed by the Matcher class, in addition to the actual column values, to compute the match. Zero or more arg elements may need to be specified


Examples

Specifying a perccentage matcher with 20% tolerance


 <match col-id="1-2"
   matcher="net.sourceforge.sqlunitmatchers.PercentageRangeMatcher">
   <arg name="pc-tolerance" value="20" />
 </match> 
      


The field Tag


Description

The field tag is used to describe a field of a user-defined data type (UDT) object returned as a STRUCT type. The returned object is treated as a JavaBean, and the fields are the bean properties. Nested UDT objects can be specified using a struct tag as the body of the field. Fields with null values have NULL as their body.


Syntax


field ::= (BODY)|(struct)
      


Attributes

Table 55. Attributes for field

NameDescriptionRequired
name name of the field/(bean property) Yes


Nested Elements

Table 56. Nested Elements for field

NameDescriptionRequired
struct A field whose type is another UDT instead of a 'simple' type is specified by a nested struct tag. Like top-level UDTs, the type and class must be specified in a typedef tag. Zero or one


Examples

Returning nested UDTs as STRUCTs.


 <struct>
   <field name="capacity">30</field>
   <field name="handler">     <struct>
       <field name="name">AEF Gripper</field>
       <field name="workLoad">12.5</field>
     </struct>
   </field>
   <field name="storage">NULL</field>
 </struct>
 
      


The sub Tag


Description

The sub tag calls a partially defined stored procedure or SQL call in a preceding subdef tag.


Syntax


sub ::= 
      


Attributes

Table 57. Attributes for sub

NameDescriptionRequired
lookup Specifies the name of the subdef element to look up. Yes
connection-id The connection-id to use or use the default connection if not specified. No


Nested Elements

Table 58. Nested Elements for sub

NameDescriptionRequired
subparam Specifies a value for the parameter for the stored procedure or SQL call that was declared in the subdef tag. Lookup is by parameter name. Zero or more


Examples

Calling a sub with parameters


 <sub lookup="UpsertStreet">
   <subparam name="street" value="Market Street" />
 </sub> 
      


The exception Tag


Description

Specifies an expected exception with expected error code and message


Syntax


exception ::= (code)?, (message)?
      


Nested Elements

Table 59. Nested Elements for exception

NameDescriptionRequired
code The error code for the expected exception No
message The error message for the expected exception No


Examples

An exception declaration


 <exception>
   <code>0</code>
   <message>ERROR: Cannot insert a duplicate key into unique index ux2_employee</message>
 </exception> 
      


The diff Tag


Description

The diff element is used to compare the results of two SQL queries or stored procedure calls. This scenario can be used to the results of a database load or when the results are either too long or unknown at the time the test specification is being written. The two queries/calls can either use the same Connection or use their own dedicated Connection to the same or different databases. The result tag is not specified in case of diff. SQLUnit will internally generate the two Result objects and match them. The default matching is exact equality, but can be overriden by supplying one or more match elements in the diff element. More information on Matching can be found in the User-Defined Matching section


Syntax


diff ::= ((skip)?, (classifiers)?, (match)*, (prepare)?, (sql|call), (sql|call))
      


Attributes

Table 60. Attributes for diff

NameDescriptionRequired
name The name of the test used by SQLUnit to report progress messages in its log output. Yes
assert Specifies a single assertion or a comma-separated list of assertions which must be true for this diff. No, defaults to equal if not specified
failure-message If specified, SQLUnit will print the user-supplied failure message if the test failed. No
java-object-support If specified, Java Object Support is enabled for the test No, default is false
multi-threaded If set to true, runs the calls in parallel to save time No, default is false (serial execution)


Nested Elements

Table 61. Nested Elements for diff

NameDescriptionRequired
skip Indicates whether the diff should be skipped or not. No
classifiers Allows user to provide classification criteria for the diff which SQLUnit can use to decide whether it should run the diff or not based on criteria provided to match the classifier. No
match Provides overrides for the matching strategy on a per column basis No
prepare Allows per test setup calls No. Zero or one prepares can be specified.
sql Specifies the SQL statement to execute. Either one of sql or call needs to be specified. Two and only two needs to be specified
call Specifies the stored procedure to execute. Either one of sql or call needs to be specified. Two and only two needs to be specified.


Examples

Diff call with multiple match elements


 <diff name="Diffing different resultset/multiple matchers"
     failure-message="Diff test #3 failed">
   <match col-id="1" 
     matcher="net.sourceforge.sqlunitmatchers.AllOrNothingMatcher">
     <arg name="match" value="true" />
   </match>
   <match col-id="2"
     matcher="net.sourceforge.sqlunitmatchers.RangeMatcher">
     <arg name="tolerance" value="50" />
   </match>
   <match col-id="3" 
     matcher="net.sourceforge.sqlunitmatchers.PercentageRangeMatcher">
     <arg name="pc-tolerance" value="10" />
   </match>
   <sql connection-id="1">
     <stmt>select widget_name, price_per_unit, number_sold from widgets where widget_id=?</stmt>
     <param id="1" type="INTEGER">1</param>
   </sql>
   <sql connection-id="2">
     <stmt>select widget_name, price_per_unit, number_sold from widgets where widget_id=?</stmt>
     <param id="1" type="INTEGER">2</param>
   </sql>
 </diff> 
      


The category Tag


Description

Allows caller to group a test by category


Syntax


category ::= (BODY)
      


The arg Tag


Description

Carries argument values and types for the containing element


Syntax


arg ::= (EMPTY)
      


Attributes

Table 62. Attributes for arg

NameDescriptionRequired
name The name of the argument to be used Yes
value The value of the argument Yes
type The type of the argument No, but required in methodArgs and constructorArgs


Examples

Specifying args for jndi


 <arg name="java.naming.factory.initial"
   value="org.jnp.interfaces.NamingFactory" />
 
      

Specifying args for methodArgs


 <methodArgs>
   <arg name="col1" type="java.lang.String" value="ABC" />
 </methodArgs>
 
      


The sql Tag


Description

The sql tag describes a regular SQL statement (not a stored procedure) with or without replaceable parameters. It can be used to describe a stored function call for databases that support it. It is converted internally to a JDBC PreparedStatement object. SQL specified by the sql statement can return a result with a single resultset or an updatecount.


Syntax


sql ::= (stmt, (param)*)
      


Attributes

Table 63. Attributes for sql

NameDescriptionRequired
id The name or number of the sql call No
connection-id When multiple Connections are defined in a SQLUnit test, this attribute refers to the Connection to use for running this call. If not specified, SQLUnit tries to look up the default Connection. No


Nested Elements

Table 64. Nested Elements for sql

NameDescriptionRequired
stmt Specifies the actual SQL statement, with or without replaceable parameters in the body of the tag. Yes
param Occurs once for each replaceable parameter, if specified, for the SQL string. Yes, if there are replaceable parameters in the SQL string.


Examples

Example of a simple sql tag


 <sql>
   <stmt>select custId from customer where custId=?</stmt>
   <param id="1" type="INTEGER">1</param>
 </sql> 
      


The dynamicsql Tag


Description

The dynamicsql tag invokes a named method in a named class in order to generate a SQL string dynamically, which is then fed to SQLUnit for testing against a known result. This was included because it is often necessary to test dynamic SQL that is built in response to known conditions, and the results are known, but the actual SQL may not be of interest.


Syntax


dynamicsql ::= ((constructorArgs)*, (methodArgs)*)
      


Attributes

Table 65. Attributes for dynamicsql

NameDescriptionRequired
static If set to true, specifies that the method is a static method No, default is false (non-static method)
class Specifies the full class name where the method is defined Yes
method Specifies the method name to invoke Yes
id The name or number of the sql call No
connection-id When multiple Connections are defined in a SQLUnit test, this attribute refers to the Connection to use for running this call. If not specified, SQLUnit tries to look up the default Connection. No


Nested Elements

Table 66. Nested Elements for dynamicsql

NameDescriptionRequired
constructorArgs Wraps a set of arg elements which specify the arguments the constructor for the class will take Not required if this is a static method, or if the class has a default null constructor
methodArgs Wraps a set of arg elements which specify the arguments to pass to the method Not required if the method does not take arguments


Examples

A dynamicsql tag that invokes a static method


 <dynamicsql class="net.sourceforge.sqlunit.utils.TestArgument"
   static="true" method="getGeneratedOracleSqlString">
 </dynamicsql> 
      


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>

          


Getting Connections


Basic JDBC Connection

SQLUnit can build up a database Connection object using JDBC if the driver, URL, user and password are supplied. Here is an example of setting up a Connection object for the SQLUnit Mock Database.


<connection connection-id="1">
  <driver>net.sourceforge.sqlunit.test.mock.SQLUnitMockDriver</driver>
  <url>jdbc:mock:net.sourceforge.sqlunit.test.mock.SQLUnitMockDatabase</url>
  <user>defaultuser</user>
  <password>defaultuser</password>
</connection>
      


JDBC Connection with properties

Certain old database drivers will complain about not being able to connect to the database if the user and password are not embedded in the JDBC URL itself. Certain others allow parameters other than user and password to be passed on the URL as a query string, so the user can set additional properties. In either of the two cases, you should simply embed all the properties in the URL element and leave the user and password elements empty. Here is an example:


<connection connection-id="3">
  <driver>net.sourceforge.sqlunit.test.mock.SQLUnitMockDriver</driver>
  <url>jdbc:mock:net.sourceforge.sqlunit.test.mock.SQLUnitMockDatabase;user=none;password=none</url>
  <user />
  <password />
</connection>
      


Basic JNDI Connection

SQLUnit can also lookup a DataSource object from a JNDI server, and get the database Connection object from that. You will need to specify the data source name to lookup, and the JNDI arguments. Refer to the documentation for your JNDI server to determine what the JNDI arguments should be. Here is an example of setting up a Mock JNDI server that SQLUnit uses for its tests.


<connection connection-id="2">
  <datasource>jdbc/mockDSN</datasource>
  <jndi>
    <arg name="java.naming.factory.initial"
        value="net.sourceforge.sqlunit.test.mock.MockInitialContextFactory" />
  </jndi>
</connection>
      


JDBC Connection using local JAR file

SQLUnit can also instantiate a driver class from a JAR or class file that is not in SQLUnit's classpath. This feature was originally built to allow SQLUnit to regression test a newer version of a JDBC driver against the older version. Since the class names were identical, SQLUnit had to load them in different contexts. SQLUnit uses a non-delegating URLClassLoader to load the driver class from the JAR or class file specified. JDBC Driver JAR files are normally self-contained, so you will typically have to specify only one single JAR file, but you can also specify a comma-separated list of file URLs containing paths to JAR files and directories containing class files. An example of the connection setup used for mock testing this functionality follows:


<connection connection-id="4">
  <driver>net.sourceforge.sqlunit.test.mock.SQLUnitMockDriver</driver>
  <url>jdbc:mock:net.sourceforge.sqlunit.test.mock.SQLUnitMockDatabase</url>
  <user />
  <password />
  <jarfile-url>file:build/,file:lib/mockrunner.jar,file:lib/log4j-1.2.13.jar,file:lib/commons-lang-2.1.jar</jarfile-url>
</connection>
      


Connection Properties from external file

Connection properties can also be specified in an external file. This is useful when the same tests should run in different environments against different databases. The properties are specified as the value of the extern attribute of the connection element. The property can be specified either as a relative or absolute file path, or as a resource in the application classpath. Property files can either contain properties to build a Connection using JDBC or look up a DataSource using JNDI. Here are some examples:

To set up an external JDBC connection, the properties file must contain the following:


#
# etc/external-jdbc-connection.properties
#
sqlunit.driver = net.sourceforge.sqlunit.test.mock.SQLUnitMockDriver
sqlunit.url = jdbc:mock:net.sourceforge.sqlunit.test.mock.SQLUnitMockDatabase
sqlunit.user = 
sqlunit.password = 
      

To set up an external JNDI connection, the properties file must contain the following name-value pairs:


#
# etc/external-jndi-connection.properties
#
sqlunit.datasource = jdbc/mockDSN
sqlunit.jndi.java.naming.factory.initial = net.sourceforge.sqlunit.test.mock.MockInitialContextFactory
      

The connection element to point to a properties file as a file name would look like this:


<connection connection-id="5" extern="etc/external-jdbc-connection.properties" />
      

To specify the properties file as a resource, it must be in the classpath and the connection element will look like this:


<connection connection-id="7" extern="external-jdbc-connection" />
      


Passing in a Connection object to SQLUnit

You can also embed SQLUnit inside your application, in which case you do not need to specify the connection element in the XML file at all. The only downside to this approach is that you are restricted to only a single database connection. To do this, build the Connection object in your application, and set the Connection object for the SQLUnit object. A code snippet to do this would look something like this:


    // Instantiate SQLUnit
    SQLUnit sqlunit = new SQLUnit("sqlunit");
    // set the properties
    sqlunit.setTestFile("/my/test/file.xml");
    sqlunit.setHaltOnFailure(false);
    sqlunit.setDebug(false);
    sqlunit.setReporter(new TextReporter("/my/report/file.xml"));
    // set the Connection
    sqlunit.setConnection(conn);
    // run the tests
    try {
        sqlunit.runTest();
    } catch (SQLUnitException e) {
        // report the exception
    }
      

Note that you can also choose to supply the Connection information inside the XML files themselves using any of the methods described above, and not specify the setConnection() method at all. SQLUnit will then read the Connection properties from the XML files and run the tests using these Connections.


Controlling Transactions

SQLUnit can put each Connection object in one of three transaction modes. The mode is controlled by the transaction-support attribute. The table below summarizes the key characteristics of each mode.

Table 1. Transaction Mode characteristics

Transaction-SupportAutoCommit Set?COMMIT on success?ROLLBACK on failure?
on (default)NoYesYes
offNoNoNo
implicitYesNoNo

When transaction-support=on (the default), SQLUnit turns off JDBC AutoCommit mode and ensures that each SQL or CALL is committed on success and rolled back on failure. The unit of work it assumes is the block contained in either an sql.stmt or call.stmt tag. If a different unit of work is required for your tests, you should consider setting transaction-support=off.

When transaction-support=off, SQLUnit turns of JDBC AutoCommit mode. It will not do either COMMIT or ROLLBACK. The COMMIT or ROLLBACK has to be handled by the client by adding the appropriate statements in the stored procedures.

When transaction-support=implicit, SQLUnit will turn on JDBC AutoCommit mode, but not issue any COMMITs or ROLLBACKs. The job of controlling transactions is left to the database. Some databases, such as Sybase and MS SQL Server, have modes that intelligently handle transactional units of work on behalf of the client. In situations where the database does not offer these features, the JDBC transaction behavior will be used, where every SQL statement or stored procedure call will be treated as a single transaction.

Warning

Prior to version 4.6, the transaction support feature did not work correctly. AutoCommit was turned on in both transaction-support on and off modes. The only difference between the two modes was that SQLUnit did a COMMIT or ROLLBACK after executing a SQL or CALL statement in the "on" mode, and not in the "off" mode. The problem was not noticeable unless you explicitly wanted to change the default unit of work, and found that setting transaction-support=off did not produce the results you expected. The fix in version 4.6 was to set the default AutoCommit mode to false, so transaction-support=off would not do any COMMIT or ROLLBACK on its own. However, this created a problem for Sybase tests that use the CHAINED feature (and is likely to cause similar problems for MS SQL Server tests that use TRANSACTION MODE IMPLICIT, although none have been reported at the time of this writing) with transaction-support=off. Using CHAINED or IMPLICIT implies that the caller lets the database decide when to apply a COMMIT or ROLLBACK, if the database supports it, or apply COMMIT or ROLLBACK after every call (the JDBC default). This mode requires that AutoCommit must be turned on. For this scenario, a new transaction-support mode "implicit" has been introduced in version 4.7. If you experience problems with CHAINED mode or IMPLICIT mode in Sybase or MS SQL Server, then you should try to use the transaction-mode="implicit".


Using variables


Setting explicitly from scalar

A variable "var" in a SQLUnit test specification is denoted by ${var}. The value of a single variable can be set explicitly using the scalar form of the set tag:


<set name="${var}" value="41">
      


Setting explicitly from query

You can also set the value for a group of variables using the non-scalar form of the set tag, shown below, which will set the value of ${myquery.col1} to the value returned from the SELECT statement.


<set name="${myquery}">
  <sql><stmt>select col1 from mytable where col2=45</stmt></sql>
  <result>
    <resultset id="1">
      <row id="1">
        <col id="1" name="c1" type="INTEGER">${col1}</col>
      </row>
    </resultset>
  </result>
</set>
      


Setting explicitly from Java method call

Since version 4.3, variables can also be set explicitly from the results of a Java method call, which must return a String. The set tag needs to specify the class name and the method name to invoke, and the class must be in the CLASSPATH. An example of such a call is shown below:


<set name="${var}" static="true" class="com.mycompany.sqlunit.Utilities"
    method="currentTimestamp">
  <methodArgs>
    <arg name="prefix" type="java.lang.String" value="MyPrefix" />
    <arg name="format" type="java.lang.String" value="yyyyMMddhhmmss" />
  </methodArgs>
</set>
      

In the above example, SQLUnit will call the method currentTimeStamp in the class com.mycompany.sqlunit.Utilities and populate the returned string into the ${var} variable in its symbol table. The ${var} can then be used by other tests in the suite.


public static String currentTimeStamp(String,String);
      


Setting variables from ant

Since version 4.6, assuming that SQLUnit is called as an Ant task, SQLUnit can refer to the variables in Ant's context by prefixing the variable name with "ant.". For example, if you wanted to access the Ant variable ${ant.project.name} within SQLUnit, you can refer to it as ${ant.ant.project.name}.


Setting variables implicitly

Variables can also be set implicitly when it is defined as a variable in a result tag and when it has not been previously declared (ie it does not have a value). The example below sets the value of the variable ${var} from the value returned from the stored procedure AddDept.


<test name="Adding department HR">
  <sql>
    <stmt>select AddDept(?)</stmt>
    <param id="1" type="VARCHAR">Human Resources</param>
  </sql>
  <result>
    <resultset id="1">
      <row id="1">
        <col id="1" name="adddept" type="INTEGER">${var}</col>
      </row>
    </resultset>
  </result>
</test>
      

In both the implicit and explicit cases, the variable ${var} functions like an lvalue in an assignment expression. Once the variable is set, the variable functions like an rvalue in an assignment expression. So if we had specified the scalar set tag above to appear in the setup tag for the test specification or the prepare tag for the test, then the test would compare the value that AddDept returned with the value for the variable ${var}.

The scope of the variable is an individual test specification file. So a variable that has been declared and assigned a value is available for the life of the test suite defined by a single XML file.

The only way to reset the value of a variable that has already been declared and assigned a value is to invoke the set tag for that variable again within the test specification.


Including Files


Including XML Files

When talking of included files, people commonly mean included XML in the context of XInclude or external entity references. The SQLUnit include tag represents an unfortunate choice of keyword and does not imply inclusion of external XML files. SQLUnit does not natively support inclusion of external XML files at all. If this functionality is desired, then you can use external entity references or embed XInclude tags and pre-process your test suite with a tool such as XIncluder.

Using External entity references is simple and requires no pre-processing of the test suite. Suppose you wanted to include an XML snippet that predeclares certain common operations that you wish to have executed during the setup phase of each test. The example below shows the declarations you would need to make.


<!DOCTYPE sqlunit SYSTEM "sqlunit.dtd" [
  <!ENTITY common-setup SYSTEM "file:tests/common-setup.xml">
]>
    <connection... />

    <setup>
      &common-setup;
    </setup>

    <test... />

      

The ENTITY declaration within the DOCTYPE associates the name of the entity with the file name. The entity can then be referenced in the body of the test suite as shown in the setup tag above.

Information on using embedded XInclude tags and using a pre-processor can be found at the XIncluder project. I considered calling XIncluder from within SQLUnit, but that would impose a penalty for everyone, including people who have no interest in including XML files. If you need this functionality, it is easy enough to pre-process the file using a script, and then delete the generated test suite XML file after the test is complete.


SQLUnit Include Files

Include files in SQLUnit refer to plain text files which contain a list of SQL or stored procedure calls that need to be run before or after a test suite is run.

The include file is parsed according to a set of fairly simple and hopefully intuitive rules. An include file consists of a collection of statements separated by delimiters.

The set of allowed delimiters are a free-standing slash ("/") character, a free-standing semicolon (";") character or a free-standing string "go" on its own line. In all cases other than when the SQL starts with a CREATE or REPLACE, a terminating semicolon (";") character is also supported.

The various kinds of statements supported in an include file are comments, CREATE and REPLACE statements, EXECUTE PROCEDURE calls, other SQL statements and shell calls. We will describe each of them in detail below.

Both single line and multi line SQL comments are supported. Single line SQL comments are those that begin with "--" on a new line. Multi-line comments begin with a "/*" and end with a "*/" followed by a newline. Comments are only for the benefit of the human author or user of the include file and is of no consequence to SQLUnit. Comments are discarded by the parser.

CREATE and REPLACE statements are typically used to define stored procedures, functions or triggers and can contain embedded semicolon characters at the end of the line before the definition is complete. These statements cannot be delimited with a terminating semicolon character because of ambiguity. Although CREATE and REPLACE can be used generically for DDL operations, making this rule only for stored procedures or functions would have led to a complexity in the parser grammar. It is also easier to remember about this restriction when applied to a generic set of statements rather than a very specific subset. CREATE and REPLACE statements can be and often will be multi-line calls.

EXECUTE PROCEDURE statements are those start with "EXEC ", "EXECUTE PROCEDURE ", or "{CALL ". They differ from regular SQL statements in that they need to be handled using a CallableStatement. The statement will be re-written to the form expected by the JDBC driver, ie {CALL procedurename()}, if it is not already specified in that form. The syntax rules for EXECUTE PROCEDURE statements are identical to that of regular SQL statements (described below), but SQLUnit will handle them as a CallableStatement rather than as a PreparedStatement. Any of the delimiters listed above will work with an EXECUTE PROCEDURE statement. Like CREATE and REPLACE statements, EXECUTE PROCEDURE statements can be multi-line.

Other SQL statements are those which do not fall into the two categories described above. Examples are SELECT, INSERT, UPDATE, DELETE, INDEX, DROP, etc. They are handled by SQLUnit using PreparedStatement. Any of the listed delimiters will work for these statements. Like the CREATE/REPLACE and EXECUTE PROCEDURE statements, these can also be multi-line. Some examples are listed below:


-- Simple select
select * from foo where bar = 1
/
-- A multi-line example
UPDATE foo 
  SET bar = 1
  WHERE bar = 2
/
      

In all the types of SQL calls described above, case does not matter, unless it is enforced by the database engine, for example, case sensitive table and column names in Sybase.

Shell calls are calls to the operating system. They are preceded with a bang ("!") character on a new line. Since Java will spawn a separate process for the shell command, it has no knowledge of the user's environment. It is generally recommended to call a shell script or batch file with the full path name and source the environment within the script. In case you wish to use the shell metacharacters such as the pipe ("|") or redirection metacharacters, it must be called within the context of the shell, with the actual command in parenthesis. As before, the full path to the command processor will need to be supplied. Multi-line commands can be specified using backslash ("\") character. Any of the delimiters listed above can be used. Here are some examples of a shell call.


-- Example of a shell script call
!/path/to/shell/script.sh
/
-- Example of a call using the shell
!/bin/sh -c (rm -rf /tmp/*.test; ls -l | wc -l >/tmp/xyz)
/
-- Example of a call using the Windows NT command processor
!COMMAND.COM /C (DEL C:\TMP\*.TST)
/
-- Example of a call using Windows XP command processor
!CMD.EXE /C (DEL C:\TMP\*.TST)
/
      

In all cases, the parser makes no attempt to validate or parse the SQL or shell script. If SQLUnit encounters an error while running the statement, it will return the stack trace to the caller to aid in debugging.

The BNF grammar (generated by jjdoc) for the Include File Parser is shown below. The comments come from inline comments in the parser grammar file, and are terse versions of the description above.


DOCUMENT START
NON-TERMINALS
// An include file is a collection of statements separated by one of the
// available delimiter characters.
	StatementCollection	:=	( CallableSQLStatement | ShellCallStatement | OtherSQLStatement | MultilineSQLStatement | CommentStatement )* ( <EOF> )?
// Comment statements can either be single line or multiline.
	CommentStatement	:=	( SingleLineCommentStatement | MultiLineCommentStatement )
// Single line comments begin with a "--" string on a newline and are 
// terminated by newline.
	SingleLineCommentStatement	:=	( <START_SINGLELINE_COMMENT> ( <SINGLELINE_COMMENT_CHAR> )* <END_SINGLELINE_COMMENT> )
// Multi line comments begin with "/*" on a newline and can contain
// multiple lines. They are terminated with a "*/" string at the end
// of the line.
	MultiLineCommentStatement	:=	( <START_MULTILINE_COMMENT> ( <MULTILINE_COMMENT_CHAR> )* <END_MULTILINE_COMMENT> )
// Multi line SQL statements are those that have a semi-colon (";") character
// terminating lines within the multi-line SQL. The trailing semi-colon 
// delimiter is not allowed for this type of statement.
	MultilineSQLStatement	:=	( <START_MULTILINE> ( <MULTILINE_CHAR> )* <MULTILINE_DELIMITER> )
// A Callable SQL statement is one which starts with "EXEC(UTE PROCEDURE) "
// or "{CALL ". The execute procedure call will be rewritten to a CALL form,
// since that is the form all JDBC drivers understand. The handler will 
// use a java.sql.CallableStatement to handle this call.
	CallableSQLStatement	:=	( ( <START_EXEC_PROC_CALL> | <START_EXEC_PROC_OTHER> ) ( <EXEC_PROC_CHAR> | <EXEC_PROC_OPEN_PAREN> | <EXEC_PROC_CLOSE_PAREN> | <EXEC_PROC_CONT_CHAR> )* <EXEC_PROC_DELIMITER> )
// A Shell call statement represents a call to an operating system command
// or external utility. This may be useful for cleaning up temporary files
// or making SQL calls through the database client of your choice. A statement
// can span multiple lines using the backslash (\) continuation character. 
// The parser treats the shell call as a system process and will start up a 
// Java Runtime object for it. Note that the runtime will have no knowledge
// of the environment settings, so you need to provide full path names. Special
// features of the shell such as pipes and redirectors will also not work 
// unless you wrap the command in the shell processor, like so:
//   !/bin/sh -c (command) for Unix/Linux
//   !command.com /c (command) for Windows XP
//   !cmd.exe /c (command) for Windows NT
// The parenthesis are required. The recommended procedure would be to 
// simply invoke a shell script which does what you want as well as source
// environment variables that you may need.
	ShellCallStatement	:=	( <START_SHELL_CALL> ( <SHELL_CALL_CHAR> | <SHELL_CONT_CHAR> | <OPEN_PAREN> | <CLOSE_PAREN> )* <SHELL_CALL_DELIMITER> )
// The Other SQL statement represents an SQL statement that is not a CREATE
// or REPLACE call (MultilineSQLStatement) or an EXEC(UTE PROCEDURE) call
// (CallableSQLStatement). SQL statements such as SELECT, INSERT, UPDATE,
// DELETE, RENAME, DROP, etc, fall into this category. The SQL statement
// can contain newlines which will be transformed by the parser into white
// space. The parser will use a java.sql.PreparedStatement to process this
// statement.
	OtherSQLStatement	:=	( ( <START_OTHER_SQL_CHAR> | <OTHER_SQL_CHAR> )* <OTHER_SQL_DELIMITER> )

DOCUMENT END

      


Error Messages

The error messages that SQLUnit will generate if there is a problem are listed below, along with possible corrective actions:

Table 1. SQLUnit Error Messages

Error MessageCorrective Action
  
Input Test File not specified, specify testfile or fileset. {usage}Neither the test file or a fileset (in the sqlunit Ant task) was specified to SQLUnit. This is a usage issue and the usage message that follows will indicate how to supply the XML file to SQLUnit
Test File {filename} not found. {usage}The test file specified could not be found by SQLUnit. Check to see if the test file exists as specified on your operating system
Input Test File specified twice, specify either file or filesetThe test file name was specified both in the testfile attribute as well as the fileset nested element. Use one or the other, not both
Cannot read Test File {filename}SQLUnit does not have sufficient permissions to read the specified test file. Fix the permissions on the file at the operating system level
Cannot build Connection (id={id}) because {reason}SQLUnit could not build the Connection object with the properties supplied. Verify that the properties for building the Connection are valid.
{resource} not found in specified ContextSQLUnit could not find the named class or resource in the specified Context. The Context could be the system classpath, the path specified by the jarfile-url attribute if that is specified, or the Naming Context if Connection is being looked up from a JNDI server.
Included file {filename} not foundThe include file specified in the include tag in either the setup or teardown tags could not be found. Check to see if the file exists at the specified location on your operating system.
System Error: Element {element} is nullThis is a system error and means that there is a problem with the code. Log a bug against the SQLUnit project with details on how to reproduce it.
System Error: Connection {id} is null or incorrectly specifiedThis is a system error and means that there is a problem with the code. Log a bug against the SQLUnit project with details on how to reproduce it.
Match Exception in class: {classname}, message: {exception_message}This error message may be encountered when using a User-defined matcher class in conjunction with the diff tag. A Matcher class always throws this type of exception if it did not get the inputs it expected or if it encounters an unexpected exception at runtime. The exception_message provides more information as to what the problem is. Usually, it can be fixed by passign arguments correctly to the Matcher class.
Cannot parse match pattern: {pattern}The values for the resultset-id, row-id and col-id attributes to the Match tag can either be specified as exact numbers, a comma-separated enumeration or a range. It can also be omitted altogether or be specified as a *, both of which imply not to match against the particular filter. The error message means that the pattern was incorrectly specified or could not be parsed for some reason. The pattern will need to be modified to conform to the rules described above.
Matcher class {className} not available in CLASSPATHSome simple matchers are supplied as part of the SQLUnit package. Matchers can also be written by users in their own package and be referenced from the SQLUnit tests provided the user-written Matcher exists in the user's CLASSPATH. The message indicates that it is not. Modify the CLASSPATH to include the user-defined Matcher class.
No match on variable at [rset,row,col]=[ {rset},{row},{col}] using class {matcher_class_name} expected: {expected_result} but got {actual_result}This is a variation of the standard message reported in case of test failures. This message is reported only by the Diff tag since that is the only tag which allows user-defined matching. In addition to the information reported by the test failure, it also reports the class name of the Matcher class currently being used. Corrective action is to fix the test.
Partial Match impossible at {position}: {reason}Partial match at the specified position could not be done because of the reason specified.
At {position}, could not convert {actual_value} to {java_class_name} for {SQL_Type_Name}({SQL_Type})The String value supplied in the param element could not be converted to the appropriate class dictated by the datatype mapping for that variable. You will most likely need to provide an override mapping specific to your database, or, if there is no suitable mapping, you/we could write a mapping class to handle this type.
At {position}, could not convert {java_class_name} returned from database to {SQL_Type_Name}({SQL_Type})The Object returned from the database is not of the same class that the class mapped to the SQL Type SQL_Type wraps. You will most likely need to provide an override mapping for your database, or, if there is no suitable mapping, you/we could write a mapping class to handle this type.
At {position}, datatype {SQL_Type_Name}({SQL_Type}) is not supportedThe specified datatype is one of the standard java.sql.Types but does not have an explicit mapping to a class. You will most likely need to provide an override mapping for your database based on your knowledge of the type, or, if there is no suitable mapping, you/we could write a mapping class to handle this type.
No type mapping found for (server.)type {{server_name}.{datatype}}SQLUnit could not find a mapping type for this specified type name. This is a non-standard (not in java.sql.Types) datatype, for which an override needs to be set in the mapping file for your database in usertypes.properties. If none of the existing mapping classes seem suitable, then you/we may have to write a new mapping class and map this to the datatype.
Parsing of {text} failed because {reason}SQLUnit could not find the variable defined in the symbol table. If you do not mind missing variables, then you should set partialOk to true.
The symbol could not be found in the symbol tableThe named symbol could not be found. Check your logic and verify that you have set the symbol before trying to use it.
Parameter {parameter_name} is not definedThe named parameter appears in the subdef as unspecified, but was not specified in the corresponding sub.
Statement {statement} ({number}) in include file {filename} returned result {error_code}A SQL Statement in the include file referenced by the file name failed with the given error code. The SQL statements are indexed starting at 1.
Variable {variable_name} is invalid, should be in the format \${variable_name}SQLUnit only accepts variables in the format ${variable_name}. Fix the name in the XML input file.
Value at outparam id={id} is a CURSOR, not a {supplied_type}The type specified in the param declaration for a stored procedure or SQL statement was incorrect. Fix the type in the XML input file.
Value at outparam id={id} is a STRUCT, not a {supplied_type}The type specified in the param declaration for a stored procedure or SQL statement was incorrect. Fix the type in the XML input file.
Value {supplied_value} for {variable} could not be converted to a numeric valueSQLUnit expects a numeric value at the location indicated, but parsing it from its String value to the appropriate numeric value resulted in a NumberFormatException.
Could not convert to digest because {reason}The reason will give more information as to why the digestion process failed. Large Objects such as BLOBs and CLOBs are digested and then compared with specified MD5 Checksums of files or the MD5 Checksum itself. If Java Object Support is enabled, and the large object represents a Serializable Java Object, or the datatype of the LOB is JAVA_OBJECT, then the object's toString() method will be called and the results returned.
Cannot invoke method {className}.{methodName}This will usually be encountered when using the methodinvoker or dynamicsql tags, which depend on the results of invoking a specified method in a specified class in the CLASSPATH. The error will identify where the problem is happening, and will also include the error stack trace within the exception. To get the stack trace, rerun the test with debug set to true, and fix the problem that is causing this error to appear.
Assertion {assert} is not supportedThe assertion provided in the assert attribute for this test element is not supported. Please check the documentation for a list of supported assertions.
Assertion {assert} failed ({reason}), expected {expected_result}, but got {actual_result}, {failure_message}The specified assertion failed.
Expected to complete in {expected-duration} ms +/- {percent-tolerance}%, but took {actual_duration}msThe test took either too little or too much time compared with the specified expected time. If the percent tolerance is provided for the test, then SQLUnit will check for specified time +/- the tolerance, else it will use a default tolerance of 10%. If it is taking too much time, it is possible that your new implementation is not using all the optimizations that your old implementation was taking advantage of. If it is taking too little time, it is possible that your stored procedure is using better optimizations or not doing everything it should. It could also be that your expected durations are now incorrect because of server-level tuning, or that the JVM is overloaded. There could be other causes for this discrepancy which are not covered here, which may be related to your site.
SQLUnit Tests failed, file {filename}, run: {total_tests_run}, failures: {number_of_failed_tests}, errors: {number_of_errors_encountered}This message is reported at the end of each SQLUnit test file if there was an error or failure running the tests in the file.
One or more SQLUnit Tests failed, see the {console|logfile} for detailsThis message is reported at the end of an SQLUnit run, spanning one or more test files, if there was a failure or an error in any one of the tests. The output will describe the exact nature of the failure(s).
{Type} error ({Exception_Class}): {message}The entire error message is returned, usually from the JVM. The exception class specifies the Exception class name. Log a bug against the SQLUnit project if the meaning is not clear enough, otherwise take the necessary action to fix the condition that is causing it. Turning on the debug attribute for the sqlunit ant task will also show the stack trace for reporting or for analysis.
  


Supported Datatypes


List of currently supported datatypes

The Database datatypes supported by SQLUnit are described below. Type classes are the names of the class used by SQLUnit to provide support for that datatype. The Type name is the name of the datatype that is used in the SQLUnit test specification. The Server is the database server for which the datatype is defined. A Server name of Any indicates global support across all databases. The Allow Input, Allows Output and Is Sortable columns indicate whether you can use these datatypes as inputs (in your param element) or as outputs (in your result specifications), and whether you can sort by columns with these datatypes (using the sort-by attribute of the resultset element).

Table 1. SQLUnit Supported Datatypes

Type ClassType NameServerAllows InputAllows OutputIs SortableWrapper for
TextTypeNTEXTMicrosoft SQL ServerNoYesNojava.io.InputStream
ClobTypeCLOBAnyNoYesNojava.sql.Clob
ClobTypeLONGVARCHARMySQLNoYesNojava.sql.Clob
ArrayTypeARRAYAnynoyesnojava.sql.Array
JavaObjectTypeJAVA_OBJECTAnyNoYesNojava.lang.Object
UnsupportedTypeDATALINKAnyNoNoNon/a
UnsupportedTypeDISTINCTAnyNoNoNon/a
UnsupportedTypeNULLAnyNoNoNon/a
UnsupportedTypeREFAnyNoNoNon/a
UnsupportedTypeSTRUCTAnyNoNoNon/a
IntegerTypeINTEGERAnyYesYesYesjava.lang.Integer
IntegerTypeSMALLINTSybase ASAYesYesYesjava.lang.Integer
BinaryTypeBINARYAnyNoYesNojava.io.InputStream
BinaryTypeLONGVARBINARYAnyNoYesNojava.io.InputStream
ByteTypeTINYINTAnyYesYesYesjava.lang.Byte
TimeTypeTIMEAnyYesYesYesjava.sql.Time
OracleCursorTypeCURSOROracleNoYesNojava.sql.ResultSet
DateTypeDATEAnyYesYesYesjava.sql.Date
LongTypeBIGINTAnyYesYesYesjava.lang.Long
ByteArrayTypeBINARYAnyNoYesNobyte[]
ByteArrayTypeVARBINARYAnyNoYesNobyte[]
BigDecimalTypeDECIMALAnyYesYesYesjava.math.BigDecimal
BigDecimalTypeNUMERICAnyYesYesYesjava.math.BigDecimal
BlobTypeBLOBAnynoyesnojava.sql.Blob
ShortTypeSMALLINT YesYesYesjava.lang.Short
OtherTypeOTHERAnyNoYesNojava.lang.Object
DoubleTypeDOUBLEAnyYesYesYesjava.lang.Double
TimestampTypeTIMESTAMPAnyYesYesYesjava.sql.Timestamp
BooleanTypeBITAnyYesYesYesjava.lang.Boolean
BooleanTypeBOOLEANAnyYesYesYesjava.lang.Boolean
StringTypeCHARAnyYesYesYesjava.lang.String
StringTypeLONGVARCHARAnyYesYesYesjava.lang.String
StringTypeVARCHARAnyYesYesYesjava.lang.String
StringTypeNCHARMicrosoft SQL ServerYesYesYesjava.lang.String
StringTypeNVARCHARMicrosoft SQL ServerYesYesYesjava.lang.String
StringTypeFIXED_CHAROracleYesYesYesjava.lang.String
FloatTypeREALAnyYesYesYesjava.lang.Float

Warning

Starting with version 4.3, the base type mappings have been slightly modified to conform with the usage recommendations from Sun Microsystems. Details of the changes are given below.

Table 2. Changes to base type mappings

Data TypeOld MappingNew Mapping
FLOATFloatType (java.lang.Float)DoubleType (java.lang.Double)
NUMERICDoubleType (java.lang.Double)BigDecimalType (java.math.BigDecimal)
VARBINARYBinaryType (java.io.InputStream)ByteArrayType (byte[])

As a result, the overrides for adaptive_server_anywhere.NUMERIC, adaptive_server_enterprise.NUMERIC, sybase_sql_server.NUMERIC, oracle.NUMERIC, postgresql.NUMERIC and mysql.FLOAT have been removed, since these mappings now conform to the corrected base type mappings.


Extending SQLUnit: Adding support for your own datatypes

Differences in implementation among different JDBC drivers and databases will require you to add support for your database if your datatype is not listed above, or if it uses a different native type from that listed. SQLUnit allows you to define these outside the package in a properties file usertypes.properties which must be in your CLASSPATH. Some common scenarios are covered below:


Using an existing implementation for an unlisted datatype

For example, assume you wished to use the Sybase ASE DATETIME datatype which behaves in much the same way as a TIMESTAMP. You can add the information for SQLUnit to support the DATETIME in the usertypes.properties files as sshown below:


# usertypes.properties
adaptive_server_enterprise.DATETIME.class = net.sourceforge.sqlunit.types.TimestampType
adaptive_server_enterprise.DATETIME.type = 93
        

The adaptive_server_enterprise is the name we get from Connection.getMetaData().getDatabaseProductName() when we open a connection to a Sybase ASA server. Actually it is "Adaptive Server Enterprise", but SQLUnit lowercases it and replaces embedded whitespace with underscores. Both entries are required. You may need to verify these values. When these values are added to the usertypes.properties file, SQLUnit will read them in after it reads its own types.properties file, and DATETIME will now work like TIMESTAMP for Sybase ASA.


Overriding the implementation for a datatype

PostgreSQL treats NUMERIC types as BigDecimal (and so does Oracle), while the standard mapping for NUMERIC is Double. To override this for PostgreSQL, we need to add the following entry in your usertypes.properties. (This is already added in to types.properties, see the table above).


# usertypes.properties
postgresql.NUMERIC.class = net.sourceforge.sqlunit.types.BigDecimalType
postgresql.NUMERIC.type = 2
        

This will override the mapping for NUMERIC when the current server is PostgreSQL.


Adding a new implementation for a new datatype

Suppose that in the DATETIME example, we wanted to input and output the DATETIME in a different format than the default. The default is "yyyy-MM-dd HH:mm:ss.SSS" while we want "MMM dd, yyyy HH:mm:ss Z". We will need to write a new type class for this and map the new type class to the DATETIME type in our usertypes.properties file. Looking at the TimestampType class, we notice that all we need to do is to override the PATTERN variable to make this work, like so:


// DateTimeType.java
package com.myapplication.mypackage;

import net.sourceforge.sqlunit.types.TimestampType;

/**
 * Models a Sybase DATETIME type.
 */
public class DateTimeType extends TimestampType {
    // just use this new pattern to parse and format
    protected static String PATTERN = "MMM dd, yyyy HH:mm:ss Z";
}
        

And the user.properties file will now look like this:


# user.properties
adaptive_server_enterprise.DATETIME.class = com.myapplication.mypackage.DateTimeType
adaptive_server_enterprise.DATETIME.type = 93
        


SQLUnit Assertions


List of currently supported Assertions

Prior to version 4.0, SQLUnit only allowed for matching two results for equality, or for matching specific columns using a matching criterion defined by one or more MatchPattern objects. SQLUnit now allows the caller to specify an assertion that should be satisfied for a given test as an attribute of the test, diff and batchtest tags. The change is backward compatible, so if the assert attribute is not specified, it defaults to "equal" in case of diff and test, and "batch-equal" in case of batchtest. Both these default assert attributes are actually macro-assertions, and are composed of a comma-separated series of assertions. These assertions, along with some new ones, can be specified either individually or in a sequence that makes sense in the context of the test case.

The assertions that are currently supported by SQLUnit are shown below:

Table 1. SQLUnit Assertions

Assert-StringDescriptionUsed In
noneAsserts nothing about the generated and the actual results. This is typically used to disable assertions for the particular test.diff,test
equalAsserts that the two results are equal. This is actually a macro assertion, which resolves to a number of subassertions, which are asserted serially. This is the default assert if not specified in a diff or test tag.diff,test
not-equalAsserts that the two results are not equal. This is an inversion of the equals assertion.diff,test
exception-matchesAsserts that the two results have identical exceptions.diff,test
not-exceptionAsserts that the result returned is NOT an exceptiondiff,test
number-outparams-equalAsserts that the two results have the same number of outparam elements.diff,test
outparams-equalAsserts that the outparams in both the results are equal.diff,test
update-counts-equalAsserts that the update counts in the two results are equal.diff,test
number-of-resultsets-equalAsserts that the number of resultsets in the two results are equal.diff,test
resultsets-equalAsserts that the resultsets are equal, or match if the test contains embedded match tags.diff,test
resultset-values-equalAsserts that the columns in the specified and actual resultset have the same values. No assertion is made for types.diff,test
resultset-types-equalAsserts that the columns in the specified and actual resultsets are of the same type.diff,test
fail-with-failureAsserts that the test will fail with the specified error message.diff,test
noneAsserts nothing about the generated and the actual batch results. This is typically used to disable assertions for the particular test.diff,test
equalAsserts that the two batchresults are equal. This is a macro assertion, composed of a sequence of assertions, which are executed serially. This is the default assertion if no assert attribute is supplied for the batchtest tag.batchtest
not-equalAsserts that the two batchresults are not equal. This is the inverse of the equal assertion.batchtest
failed-at-equalAsserts that the failed-at attribute of the batchresult tag, if specified, points to the point where the result generated by the batchcall or batchsql actually failed.batchtest
expected-count-equalAsserts that the number of update counts specified in the expected batchresult, either as an expected-count attribute, or listed explicitly, is the same as the one in the generated batchresult.batchtest
updatecount-equalAsserts that the updatecount values are the same for the result specified by batchresult and the result generated by the call to batchsql or batchcall.batchtest
fail-with-failureAsserts that the test will fail with the user-supplied failure message.batchtest


Extending SQLUnit: Adding your own Assertion

Assertions can be of two kinds, one specific to the batchtest tag (involving BatchDatabaseResult objects) and the other specific to diff and test tags (involving DatabaseResult objects). The following things need to be done to add a new assertion.

  1. Determine if the tag is for the batchtest tag or the diff/test tags.

  2. Add an entry for the mapping from the assert string to the actual method name to invoke.

  3. Implement the method.

This can be most easily explained by means of an example. Suppose we wanted to implement an assertion "in-same-ballpark" for DatabaseResult objects. We wish to map this assertion to the method assertInSameBallpark(). The mapping to set up is shown below.


    private static final String[] DATABASE_RESULT_ASSERT_MAPPING = {
        {"equal", "assertEqual"},
        // more mappings here
        {"in-same-ballpark", "assertInSameBallpark"}
    };
      

We then implement the logic to determine if two DatabaseResult objects are in the same ballpark. This involves adding a method and implementing it, like so:


    private static void assertInSameBallpark(String failureMessage,
            DatabaseResult expR, DatabaseResult gotR, List matchPatterns)
            throws SQLUnitException {
        // some logic here. If the test fails it should throw an
        // SQLUnitException with an ASSERT_FAILED message.
    }
      

Look at the code for Assertions.java for more information.


Large Object (LOB) Support


Handling Object output from ORDBMS

Databases such as PostgreSQL allow storing non-primitive database types, both built-in such as the geometric data types, or user-defined. These datatypes can be returned from stored procedure calls. As long as a toString() method is defined for these objects, all that needs to be done is to specify the String representation of this object in your expected result. For example, a PostgreSQL POINT data type is backed by the org.postgresql.geometric.PGPoint object, which defines a toString() method. So a POINT(x,y) can be represented by the String (x,y). So to test that a POINT(10.0, 20.0) is returned as the seventh column of the first row of the first resultset from a procedure call, the XML would look something like this:


<result>
  <resultset id="1">
    <row id="1">
      <col id="1" type="INTEGER">123</col>
      ...
      <col id="7" type="OTHER">(10.0,20.0)</col>
    </row>
  </resultset>
</result>
        
        

The toString() method must be defined in order for the comparison to work, since SQLUnit compares two objects for equality based on their String representation returned from the toString() method.

Note that SQLUnit does not yet (and probably never will) support passing built-in or user-defined objects as arguments to stored procedures to be tested with SQLUnit. The reason is that it is difficult to convert from the String representation to the equivalent object representation in a generic manner. The approach taken in the PostgreSQL test (AddEmployee.sql) is to pass the parameters for object creation (the x and y coordinates for the Employee.location::POINT) as separate arguments to the stored procedure. The stored procedure is responsible for constructing a POINT object and storing it into the database. This approach allows scaling to more complicated types (such as CIRCLE((x,y),r)) in a trivial manner.


Handling BLOB and CLOB outputs

Modern databases are frequently capable of storing BLOBs (Binary large objects) and CLOBs (Character large objects) as columns in their tables. Since it is hard and messy to include the expected CLOB output in the XML test file, and pretty much impossible in case of a BLOB, SQLUnit requires that you either specify the name of a file containing the data or specify the MD5 Checksum for the data as a String in your expected results element.

SQLUnit converts large objects such as CLOB, BLOB, LONGVARBINARY and LONGVARCHAR to an MD5 digest when extracting from the database via a stored procedure or SQL call. This is done in order to make subsequent comparisons faster. Other types are extracted and stored as Strings, which may be converted to an MD5 digest form at comparison time if the test matches it against a file name (prefixed with file:) or an MD5 digest (prefixed with md5:).

If you are specifying the name of the file to compare against, prepend either the full path name of the file or the relative path offset from the directory where the tests will be run, with the string file:. If specifying the MD5 Checksum for the resulting LOB data, prepend the checksum string with md5:.

SQLUnit writes CLOB and BLOB objects retrieved from the database into temporary files on disk. In case of a test failure, the mapping between the LOB column [resultset,row,col] in the failed test and the temporary file created is shown at the bottom of the test report. It is up to the user to verify what the difference is using operating system tools such as diff (for CLOBs) and cmp (for BLOBs).


Java Object Support

Java aware databases support storing Java objects in the database natively. Most relational databases are not Java aware. SQLUnit provides a way to handle serialized Java Objects as first class Java Objects by storing them as BLOB columns. This is a little different from storing native database objects stored in Object Relational Databases as discussed in the section Handling Object Output from ORDBMS. Java Objects are populated, then serialized and the bytecode generated are then stored in the database as BLOBs. When they are returned in the resultset of a stored procedure or SQL call, they can be automatically stringified using the Object's toString() method. The only requirements for the Object being stored are that it must implement java.io.Serializable and override the default Object.toString() method.

Here is a contrived example. Suppose you wanted to store a permission object in a user table, to indicate which user had access to what system resources. You could create a Dictionary Object which contained a Map of resource names to permission. So you would create a Dictionary class which implements the java.io.Serializable interface and whose toString() method you would override to create a compact but readable String representation. The source code for the Dictionary object is available at test/Dictionary.java. You would then have your application populate these objects for different users and store them in the database. The source code for the JUnit class which simulates this behavior is available at test/LOBLoader.java. The JUnit class also creates several other files in order to run the MySQL SQLUnit tests. See the README file for details on how to run the test.


User-Defined Matching


What is User-Defined Matching

Starting with version 2.7, SQLUnit provides the ability to match the results generated from two different SQL queries or Stored Procedure calls. It also provides the ability for the user to define matching strategies on a per-column basis, to override the default matching strategy. By default, SQLUnit will match two columns from two different results on the basis of exact equality, including type. Matchers match only the value, based on the matching criteria. The user can specify different behavior for one or more columns in the result, such as match when the two columns are different by a certain tolerable amount.

SQLUnit ships with some simple Matcher classes that are available in net.sourceforge.sqlunit.matchers. These can be used as-is, or can be used as the basis for more sophisticated Matcher classes with site-specific functionality.


Wildcarding rules for Match Attributes

Table 1. Wildcarding rules for the Match Attributes

PatternDescriptionExample
*Specifying a '*' for a given attribute means that SQLUnit will match all corresponding elements represented by this attribute in the result element. It is equivalent to not specifying the attribute at all.resultset-id="1" row-id="*" col-id="*" means match all rows and all columns in resultset 1
Single number (n)Specifying a single number for a given attribute means that SQLUnit will match all corresponding elements having this attribute in the result element.resultset-id="*" row-id="*" col-id="3" will match the third column in all rows in all resultsets
Range of numbers (m-n)Specifying a range of numbers for a given attribute means that SQLUnit will match all corresponding elements whose attribute falls in the specified range.resultset-id="*" row-id="1" col-id="1-3" will match the first, second and third columns in the first row for all resultsets using this matcher.
Enumeration of numbers (m,n,p)Specifying an enumeration for a given attribute means that SQLUnit will match all corresponding elements whose attributes match one of the numbers in the enumeration.resultset-id="*" row-id="1" col-id="1,2,3" will do the same thing as the above example.
Combination of Enumeration and Range (m-n,p,q)Any combination of range patterns and simple enumeration separated by commas is also supported.resultset-id="*" row-id="1" col-id="1-3,5,6" will match the first, second, third, fifth and sixth columns in the first row for all resultsets using the matcher.


List of currently supported Matchers

Table 2. SQLUnit Matchers

Matcher NameAuthorDescriptionArguments
AllOrNothingMatcherSujit Pal (spal@users.sourceforge.net)The AllOrNothingMatcher is an implementation of the IMatcher interface used to define rulesets for matching columns in SQLUnit. This implementation is of no particular use, except as a template for other useful matchers and for testing. Based on the argument supplied to it via the argument map, it will either always returns true or false for the column being matched.

match : Always returns true if set to true, else always returns false.

SignificantDigitsMatcherTim Cull (trcull@yahoo.com)The SignificantDigitsMatcher is an implementation of the IMatcher interface used to compare FLOAT values where you only really care about matching to a certain number of significant digits to the right of the decimal. Especially useful when you're generating your test script by copying and pasting out of a SQL query tool that displays a different number of significant digits than SQLUnit pulls back from the database. Example configuration: <match resultset-id="1" row-id="*" col-id="9-13,15-18" matcher="net.sourceforge.sqlunit.matchers.SignificantDigitsMatcher"> <arg name="signif-digits" value="3" /> </match>

signif-digits : Number of significant digits to match to

TypelessMatcherSujit Pal (spal@users.sourceforge.net)The TypelessMatcher matches column values only from two different SQL or stored procedure calls. This would be useful when the data returned from two different databases may have the same String value but the actual datatypes they are implemented as may be different. This is the matcher that is used implicitly when the assertion "resultset-values-equal" is called.

None : -

RangeMatcherSujit Pal (spal@users.sourceforge.net)The RangeMatcher is an implementation of the IMatcher interface used to define rulesets for matching columns in SQLUnit. This matcher will accept an absolute tolerance value and check to see that the target is within (+/-) tolerance of the source.

tolerance : An absolute tolerance value.

PercentageRangeMatcherSujit Pal (spal@users.sourceforge.net)The PercentageRangeMatcher is an implementation of the IMatcher interface used to define rulesets for matching columns in SQLUnit. This matcher will accept a percentage tolerance value and check to see that the target is within (+/-) tolerance percent of the source.

pc-tolerance : A percentage tolerance value.

TypelessPercentageMatcherChris Watts (c_watts@users.sourceforge.net)The TypelessPercentageMatcher matches columns which may differ by a specified percentage. This can be useful when comparing numeric values across data types which have different default precisions.

pc-tolerance : A percentage tolerance value.

ForgivingNullMatcherTim Cull (trcull@yahoo.com)The ForgivingNullMatcher is an implementation of the IMatcher interface used to compare values you know are either supposed to be NULL (but might be textually represented differently) or are actually equal. Basically searches for the word NULL (case insensitive) or for blank elements or for elements that are equal Strings. Especially useful when you're generating your test script by copying and pasting out of a SQL query tool that displays NULLs differently than SQLUnit pulls back from the database. For example, all of these values are treated as equal: <col id="14" name="benchcode" type="INTEGER">[NULL]</col> <col id="14" name="benchcode" type="INTEGER">NULL</col> <col id="14" name="benchcode" type="INTEGER"></col> <col id="14" name="benchcode" type="INTEGER">null</col> Example configuration: <match resultset-id="1" row-id="*" col-id="14" matcher="net.sourceforge.sqlunit.matchers.ForgivingNullMatcher"> </match>

None : -

ExpressionMatcherSujit Pal (spal@users.sourceforge.net)Allows the caller to specify an expression in Apache JEXL (Java Extended Expression Language). The expression must evaluate to a boolean expression (true or false). The source and target are referenced in the expression using the pseudo-variables expected.value and actual.value.

expression : A valid JEXL expression. Source and target variables in the expression are referenced by the pseudo-variables expected.value and actual.value respectively.


Extending SQLUnit: Writing your own Matcher

Note

If you have written a Matcher that does something unique, and would like to contribute it to the SQLUnit project, it would be gratefully accepted and credit given.

Writing your own Matcher is easy. The Matcher simply specifies the match operation by implementing the isEquals(String,String,Map) method in the net.sourceforge.sqlunit.IMatcher interface. Specifying where the matcher should be invoked can be done from the SQLUnit specification file itself.

We will illustrate the process by taking the PercentageRangeMatcher class supplied with the SQLUnit distribution and annotating it with comments that may be helpful in writing your own matcher.


/*
 * $Id: sqlunit-book.xml,v 1.100 2006/04/30 22:25:54 spal Exp $ (1) 
 * $Source: /cvsroot/sqlunit/sqlunit/docs/sqlunit-book.xml,v $
 * SQLUnit - a test harness for unit testing database stored procedures.(2)
 * Copyright (C) 2003  The SQLUnit Team
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package net.sourceforge.sqlunit.matchers; (3)

import java.util.Map;  (4)
import net.sourceforge.sqlunit.IErrorCodes;
import net.sourceforge.sqlunit.IMatcher;
import net.sourceforge.sqlunit.SQLUnitException;

/** (5)
 * The PercentageRangeMatcher is an implementation of the IMatcher interface
 * used to define rulesets for matching columns in SQLUnit. This matcher
 * will accept a percentage tolerance value and check to see that the target 
 * is within (+/-) tolerance percent of the source.
 * Arguments:
 * pc-tolerance : a percentage tolerance value.
 * @author Sujit Pal (spal@users.sourceforge.net)
 * @version $Revision: 1.100 $
 */
public class PercentageRangeMatcher implements IMatcher { (6)

    /**
     * Default constructor as per contract with IMatcher.
     */
    public PercentageRangeMatcher() {;}

    /**
     * Returns true if the value of the target is withing (+/-) a specified
     * tolerance value of the source. Note that in this case, the source,
     * target and tolerance must all be numeric values.
     * @param source the String representing the source to be matched.
     * @param target the String representing the target to be matched.
     * @param args a Map of name value pairs of arguments passed in.
     */
    public boolean isEqual(String source, String target, Map args)
            throws SQLUnitException { (7)

        String aTolerance = (String) args.get("pc-tolerance"); (8)
        if (aTolerance == null) {
            throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
                new String[] {this.getClass().getName(),
                "Value for key 'pc-tolerance' is NULL"});
        }
        // is tolerance a float?
        float iTolerance = 0;
        try {
            iTolerance = Float.parseFloat(aTolerance);
        } catch (NumberFormatException e) {
            throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
                new String[] {this.getClass().getName(),
                "Value of key 'pc-tolerance' is not a FLOAT"});
        }
        // cannot have the tolerance exceed 100
        if (iTolerance > 100.0) {
            throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
                new String[] {this.getClass().getName(),
                "Value of key 'pc-tolerance' must be between 0 and 100"});
        }
        // is the source a float? (9)
        float iSource = 0;
        try {
            iSource = Float.parseFloat(source);
        } catch (NumberFormatException e) {
            throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
                new String[] {this.getClass().getName(),
                "Value of 'source' is not a FLOAT"});
        }
        // is the target an integer?
        float iTarget = 0;
        try {
            iTarget = Float.parseFloat(target);
        } catch (NumberFormatException e) {
            throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
                new String[] {this.getClass().getName(),
                "Value of 'target' is not a FLOAT"});
        }
        // return the match (10)
        return ((iTarget >= (iSource - (iTolerance * 100))) &&
            (iTarget <= (iSource + (iTolerance * 100))));
    }
}
      

(1)
In general, if you are contributing a matcher to the SQLUnit project, please include the Id and Source CVS tags. This will allow us to track version revisions in SQLUnit. Even if you dont plan to contribute the matcher to the SQLUnit project, its a good practice to include the tags for whatever Source Code Control System you are using.
(2)
Please include this boilerplate in your code if you are planning to contribute the matcher to the SQLUnit project. This indicates that this code is now covered by the GNU General Public License. If you are not planning to contribute the Matcher, please ignore this callout.
(3)
The package statement identifies the package in which your matcher class will live in. It can be anything you want, but your CLASSPATH must contain this package when running SQLUnit with your new user-defined Matcher class.
(4)
You will need to import the following classes from the net.sourceforge.sqlunit package. IMatcher is the interface which your matcher class is implementing. SQLUnitException is the Exception class thrown by SQLUnit applications, and IErrorCodes is another interface which contains the definition of the Error Codes thrown by SQLUnit. It is important to throw SQLUnitExceptions from your Matcher class because otherwise your exceptions will not be reported by SQLUnit.
(5)
Because of the weak coupling between any Matcher and the rest of SQLUnit, the class documentation is probably the best place you have to provide the user of your Matcher class with useful information relating to the use of the Matcher. The class documentation in this case defines what the Matcher does, and provides the information about what keys need to be passed in to the Matcher for it to do its work.
(6)
Your Matcher class needs to implement the net.sourceforge.sqlunit.IMatcher interface, otherwise it will not be visible to SQLUnit.
(7)
Your Matcher class needs to implement the only method in the IMatcher interface, which is the boolean isEqual(String,String,Map). The first argument represents the source column value to match, the second the target column value, and the third is a Map of key-value pairs which contain additional information needed by the isEqual method. If the actual comparison needs to be made on numeric objects, as is the case with the PercentageRangeMatcher, then your Matcher should convert to the appropriate class.
(8)
The weak coupling between the SQLUnit code and the Matcher code is by design. It was necessiated by the need to make the Matchers be very flexible and to keep the SQLUnit XML code simple to use. As a result, the Matcher must make sure that it can use the arguments being passed to it. If not, it should abort with a SQLUnitException (IErrorCodes.MATCHER_EXCEPTION) specifying the class name and the Matcher specific exception message.
(9)
As mentioned above, the Matcher code is responsible for converting from the supplied String value to whatever form it needs. In this case, it converts the source and target String values to Floats. If conversion fails, then it should throw a SQLUnitException (IErrorCodes.MATCHER_EXCEPTION) specifying the class name and the Matcher specific exception message.
(10)
This computes the value of the match and returns it.


Setting up your Matcher for use with SQLUnit

This can best be explained by use of an example. Suppose you to specify a particular Matcher, com.mycompany.sqlunit.matchers.MyMatcher to work on the first resultset, all rows, and columns 1, columns 3 to 5 and column 7, you would embed the Match element within your Diff Element as shown below. In addition, if your matcher needed some extra information, you can supply them as a set of key-value pairs using nested Arg elements within the Match element. In our example below, the Matcher looks for the variable key1 with a value value1 in order to do the matching.


<diff name="MyDiff">
  <match resultset-id="1" row-id="*" col-id="1,3-5,7"
    matcher="com.mycompany.sqlunit.matchers.MyMatcher">
    <arg name="key1" value="value1" />
  </match>
</diff>
        
        

The above example illustrates some rudimentary wildcarding features that SQLUnit supports in order to specify more than one column for a particular matching strategy. Any columns that are not covered by the wildcarded filters default to the default matching strategy, which is to check for exact equality. The rules for wildcarding are the same for the resultset-id, row-id and col-id attributes and are detailed below. SQLUnit decides which columns qualify by checking the patterns for resultset-id, row-id and col-id additively.


User-Defined Reporting


What is User-Defined Reporting?

Starting with version 3.6, SQLUnit has the ability to use reporters that work within the context of other testing frameworks. By default, SQLUnit uses the TextReporter, which can be used to print a text report of the results of the test, either to a file specified in the sqlunit task's logfile attribute, or to STDERR if the logfile attribute is not specified. This functionality was contributed by Rob Nielsen and Paul King. They have also contributed a reporter that works within the context of the Canoo Web Test framework.

Note

If you have written a Reporter that does something unique, and would like to contribute it to the SQLUnit project, it would be gratefully accepted and credit given.


List of currently supported Reporters

Table 1. SQLUnit Reporters

NameAuthorDescription
ReporterListIvan IvanovA IReporter that contains a list of other IReporters. Used to provide reporting to multiple places.
EmptyReporterIvan IvanovAn empty reporter implementation. Useful for test cases.
TextReporterRob Nielsen (robn@asert.com.au)SQLUnit reporter that writes its text to standard output. This is the default reporter for SQLUnit.
CanooWebTestReporterRob Nielsen (robn@asert.com.au)SQLUnit Reporter that works with the Canoo Web Test framework. Generates XML that can be converted to output for the Canoo Web Test console.


Extending SQLUnit: Writing your own Reporter

To add a new User-defined reporter, simply implement the IReporter interface and add an entry into etc/reporters.properties, and recompile the distribution. The interface specifies a template with methods to be implemented that are then called by SQLUnit in a predetermined manner in the reporting lifecycle. The Javadocs provide guidance as to what point in the lifecycle that method would be called by SQLUnit, so the implementation would have to code the required behavior. Take a look at the CanooWebTestReporter and TextReporter for more details. There is also a hasContainer() method which indicates if the reporter is running within the context of another test framework or not. If it is running in the context of another test framework, then SQLUnit will not attempt to set up the Ant BuildListener objects for it.


Setting up your Reporter for use with SQLUnit

SQLUnit currently comes with the default TextReporter and the CanooWebTestReporter. To use either of these, specify the logformat attribute of the sqlunit task to be the alias for the reporter of your choice. The aliases can be found in the reporters.properties file in the etc subdirectory of the distribution. Here is an example of using the CanooWebTestReporter.


<sqlunit testfile="mytest.xml" haltOnFailure="false" debug="false"
    logfile="./output.log" logformat="canoo" />
        
        


User-Defined Test Grouping and Test Skipping


What are User-Defined Test Groups

Starting with version 4.9, SQLUnit provides to ability to classify the tests according to some user-defined criteria. One example for classification may be by test importance (severity) or by the part of the system (category) it applies to or both. This gives the ability to execute for example only tests that applies to ModuleA and with the highest priority.


Declaring the group of a test

All the grouping information of a given test is declared in <classifiers> tag. It is called in this way, because it classfies the test. One concrete example is:


  <test name="testName">
    <classifiers>
      <severity>WARN</severity>
      <category>TestForModuleA</category>
    </classifiers>
    <sql>
      <stmt>aSimpleResult>/stmt>
    </sql>
    <result>
      <resultset id="1"gt;
        <row id="1">
          <col id="1" name="col1" type="INTEGER">1</col>
        </row>
      </resultset>
    </result>
  </test>
      

This means that this test belongs to TestForModuleA category and its severity is WARN, so that if it fails it will be just a warning and not a error or a fatal situation.

The user then should provide some global criteria and each test's classification will be compared. If the test matches them it will be executed; if not will be skipped and reported as skipped. Here is a detailed explantion how the decision whether a test matches ot not is done. When <classifiers> tag is reached, its corresponding ClassifiersHandler is called, whose in turn calls the handlers of all its children. It is responsibility of the children's handler to extract the criterion they are interested in from the environment and then to compare whether their content meets that criterion. If it is not met they return false (as a Boolean object) to ClassifiersHandler. On the other hand, the latter stops its execution on the first false value it receives and the test is not executed.

For a concrete example how the user defines these global criteria see the next two sections.

SQLUnit comes with two classifying handlers (but you can provide your own too), which are SeverityHandler and CategoryHandler and the next two sections are dedicated to them.


SeverityHandler

SeverityHandler corresponds to <severity> tag, nested in <classifiers> tag. As shown in the example of the previous section the body of <severity> tag contains the severity of the test. Now the user gives the global severity, which we will call the threshold severity in one of the following way: as an ant property called sqlunit.threshold.severity, as the value of the key sqlunit.threshold.severity from SQLUnit's SymbolTable or as a System property called sqlunit.threshold.severity. SeverityHandler will take the threshold severity, the current test's severity and will execute the test iff the current test's severity is equal ot greater than the threshold severity.

The following values are valid severity value: DEBUG, INFO, WARN, ERROR, FATAL and are compared in the following way: DEBUG < INFO < WARN < ERROR < FATAL. For example if the user invokes the test from the previous section with


ant -Dsqlunit.severity.threshold=INFO
      

the test will be executed because INFO <= WARN. But if it is invoked with


ant -Dsqlunit.severity.threshold=ERROR
      

the test will not be executed as ERROR > WARN.

Since <severity> is an optional tag, the following edge cases are taken into account: if the threshold severity is not set all tests match regardless of their severities; if a test does not have assigned severity it matches, regardless of the threshold severity.


CategoryHandler

CategoryHandler corresponds to <category> tag, nested in <classifiers> tag. As shown in the example of the previous section the body of <category> tag contains the category of the test. Now the user gives the global category in one of the following ways: as an ant property called sqlunit.category, as the value of the key sqlunit.category from SQLUnit's SymbolTable or as a System property called sqlunit.category. CategoryHandler will take the global category, the current test's category and will execute the test iff they match in the terms of regular expression matching. For example if the user invokes the test from the previous section with


ant -Dsqlunit.category=TestForModuleA
      

the test will be executed. If it is invoked with


ant -Dsqlunit.category=TestForModuleB
      

the test will not be executed.

Since <category> is an optional tag, the following edge cases are take into account: if the global category is not set all tests match regardless of their category; if a test does not have assigned category it matches regardless of the global category.

The global category can be given as regular expression pattern also:

ant -Dsqlunit.category=TestForModule(.*)


Extending SQLUnit: Write your own classification

If you want to classify your tests not only by severity and category, but by other criteria, you have just to add your own handler as it described in the section Writing your handler. Note that in the case of classifying handlers, their process method should always return to ClassifiersHandler a Boolean object. Except the fact that it must receive a Boolean from its "children" the ClassifiersHandler knows nothing about the handlers it calls, thus making it more robust. If you return to it an object of another type, you will receive ClassCastException.


Skipping Tests with <skip>

Sometimes you may want to skip deliberately some test (regardless whether they match some criteria like the ones above or not). SQLUnit provides skip tag which is used in this way:


  <test name="Testing with skip=true and reason">
    <skip value="true">Reason for skipping</skip>
    <call>
      <stmt>aSimpleResult</stmt>
    </call>
    <result>
      <resultset id="1">
        <row id="1">
          <col id="1" name="col1" type="INTEGER">1</col>
        </row>
      </resultset>
    </result>
  </test>
      

The content of skip tag value attribute specifies whether the test will be skipped or not. If it is true the test is skipped and the reason for its skipping is given in the body of <skip> tag.


Using the Mock Testing Framework


Overview

The SQLUnit Mock Testing Framework is based on the JDBC mock testing framework provided by the MockRunner Project, and allows a developer to test the SQLUnit code without using a database. Its primary value would be to model databases to which members of the SQLUnit development team do not have access.

Instead of a real database, the testing framework relies on introspecting a class specified in the JDBC URL. This class must implement the IMockDatabase interface and contains one or more methods with the following signature:


public MockResultSet methodName(Integer resultSetId);
      

The actual implementation of IMockDatabase is specified in the connection parameters in the XML test file. In the example below, the "jdbc:mock" string is used by the DriverManager to select the SQLUnitMockDriver to handle the Connection, and the rest of the URL is used to identify the class name which will be used to supply the MockResultSets needed for the tests.


<connection>
  <driver>net.sourceforge.sqlunit.test.mock.SQLUnitMockDriver</driver>
  <url>jdbc:mock:net.sourceforge.sqlunit.test.mock.SQLUnitMockDatabase</url>
  <user />
  <password />
</connection>

      

The AbstracMockDatabase abstract class provides an introspecting implementation of the IMockDatabase#getResultSet() method. It is an abstract class, so it is expected that someone implementing a Mock Database using this mechanism would extend this class. This is the case with the SQLUnitMockDatabase class, which contains various methods that model mock stored procedure calls.


Writing your own mock procedures

The SQLUnitMockDatabase provides various examples of mock procedures written using the SQLUnit Mock framework, but there are a number of conventions which you should be aware of when reading through these examples.

  1. Each mock procedure will need to return various resultsets based on the resultset id supplied. Calls made to the same procedure with different values of the resultset id will result in different MockResultSet objects being returned.

  2. ResultSet 0 is always the count of the number of resultsets that can be returned from this procedure call, embedded in a MockResultSet object.

  3. ResultSet 1 through n represents the first through the n-th ResultSet objects that may be returned from the procedure.

  4. ResultSet -1 through -n represents the first through n-th Outparam objects that may be returned from the procedure. They need to be always specified as Strings, they will be automatically converted based on the Outparam definition supplied in the param element. Oracle CURSOR outparams are returned as MockResultSets.

  5. Throwing of SQLExceptions can be simulated by wrapping a SQLException within a MockResultSet at a particular ResultSet position.

  6. MockResultSetUtils contains most of the functionality of wrapping and unwrapping non-ResultSet objects into and out of MockResultSets. Consider adding functionality to this class if you do not see something you want.

Here is an example of a mock procedure that returns a single return code (OUTPARAM 1) and a single resultset consisting of 1 row with 3 columns. As described above, resultset index of -1 corresponds to the return code at outparam position 1, resultset index of 0 corresponds to the number of resultsets returned from the procedure, and resultset index of 1 corresponds to the first resultset returned from the procedure.


/**
 * Returns a result code and a single resultset.
 * @param index the result set id.
 * @return a MockResultSet at the specified index.
 */
public MockResultSet resultAndOneResultSet(Integer index) {
    if (index == null) { return null; }
    int rsid = index.intValue();
    switch (rsid) {
        case -1:
            return MockResultSetUtils.buildScalarOutParam("143");
        case 0:
            return MockResultSetUtils.buildZerothResultSet(1);
        case 1:
            MockResultSet mrs = new MockResultSet("resultAnd1ResultSet:-1");
            mrs.setResultSetMetaData(MockResultSetUtils.buildMetaData(
                new ColumnMetaData[] {
                new ColumnMetaData("agentId", Types.INTEGER),
                new ColumnMetaData("name", Types.VARCHAR),
                new ColumnMetaData("drink", Types.VARCHAR)}));
            mrs.addRow(new Object[] {
                new Integer(7), new String("James Bond"),
                new String("Martini")});
            return mrs;
        default:
            return null;
    }
}
      

More information on how the various peices fit together from a programmatic perspective can be found in the package level documentation in the Javadocs for this package.


Writing tests against the mock procedures

The process of writing tests against the mock procedure is identical to writing it for a real database. You can find some examples under test/mock/test.xml in the distribution. Here is one of them, which corresponds to the code shown above for the mock procedure.


<test name="Testing result code and one resultset">
  <call>
    <stmt>resultAndOneResultSet</stmt>
    <param id="1" type="INTEGER" inout="out">${rc}</param>
  </call>
  <result>
    <outparam id="1" type="INTEGER">143</outparam>
    <resultset id="1">
      <row id="1">
        <col id="1" type="INTEGER">7</col>
        <col id="2" type="VARCHAR">James Bond</col>
        <col id="3" type="VARCHAR">Martini</col>
      </row>
    </resultset>
  </result>
</test>