Sunteți pe pagina 1din 133

SQLUnit User Guide

Sujit Pal
Copyright 2003-2004 The SQLUnit Team

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 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 severity Tag The paramset Tag The subdef Tag The col Tag The batchtest Tag The constructorArgs Tag The batchcall Tag The outparam Tag The test Tag The resultset Tag The match 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) PostgreSQL 7.3

Porter

Status Complete. 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 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. Complete, 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. Complete, with lots of testing support from Soumya Shetty and Lakshmi Started 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. In progress 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

Sujit Pal

MySQL 3.4

Sujit Pal

Oracle

Sahib Singh Wadhwa, Andrei Grigoriev, Sujit Pal Sowmya Shetty, Lakshmi, Sujit Pal

Sybase ASE 12.5

IBM DB2

Sujit Pal

HSQLDB (formerly Hypersonic) Informix SE 7.0

Campbell BoucherBurnett

Database Name (Version)

Porter

Status working setup of Informix, then I would appreciate you running the tests and letting me know the results. On 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. Complete, test suite included with distribution (test/asa/README)

Microsoft SQL Server 2000

James Henderson, Paul King

Sybase ASA 9.0.1

David Fishburn

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. 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 Softwa re and Versio n

How it is used

Licensing

URL

Require Inclu d ded

SQLUnit is written using Java 1.4_02, so you will need to install the JRE if you Java 1.4 are just going or later to use it, or the JDK if you are going to do development with it.

Proprietary but free for download and use

http://java.sun.com/

Yes

No

Ant is used to build the SQLUnit package. An Ant 1.6 Apache License ant task is or later provided to run SQLUnit from within Ant.

http://ant.apache.org/

Yes, although you can also just No call it from the comman d line with

Softwa re and Versio n

How it is used

Licensing

URL

Require Inclu d ded java.

Junit 3.7 or later

SQLUnit is really a specialized JUnit test class. It is needed to run SQLUnit.

Common Public License Version 1.0

http://www.junit.org/inde Yes x.htm

Yes

JDOM Beta 9 or later

SQLUnit uses JDOM to do Apache License its XML parsing and rendering.

http://www.jdom.org/

Yes

Yes

Apache This is used Log4J for logging in Apache License 1.2.8 or SQLUnit. higher Apache Used by Xerces- Canoo J 1.4.4 WebTest

http://logging.apache.org/ Yes log4j/docs

Yes

http://xml.apache Yes .org/xerces-j/

Yes

SQLUnit uses this to set up and run a set of Mock tests MockR against a Apache style unner Mock license 0.2.7 database that consists of method calls which are introspected. Apache Used by Commo MockRunner ns-Lang

No. This is only needed if you want to run http://mockrunner.sourcef the mock Yes orge.net/ test suite to make sure everythi ng worked. http://jakarta.apache.org/c No, see ommons/ above. Yes

Apache License

XDocle SQLUnit uses BSD License t 1.2.1 XDoclet to

http://xdoclet.sourceforge. No, only No net/ needed

Softwa re and Versio n

How it is used convert source code annotations into Docbook XML which is then included in this document.

Licensing

URL

Require Inclu d ded to generate the documen tation.

SQLUnit uses Jalopy to provide a guide for coding conventions used in Apache SQLUnit. Jalopy BSD License Contributed 0.6.2 code typically passes through Jalopy to get reformatted before inclusion. SQLUnit uses CheckStyle to audit code style on an ongoing basis. The checks that have been Apache configured are Checkst LGPL a union of the yle 3.4 sun_checks.x ml and BlochEffectiv eJava.xml files supplied with the distribution.

http://jalopy.sourceforge. net/

No, only needed to reformat contribut No ed code before submissi on.

Yes, if you plan on http://checkstyle.sourcefo making No rge.net/ code contribut ions.

Softwa re and Versio n

How it is used Some checks have been relaxed or eliminated where it did not match the coding style for SQLUnit.

Licensing

URL

Require Inclu d ded

SQLUnit uses JavaCC to generate a parser for include files JavaCC based on the BSD License 3.2 grammar specified in the IncludeFilePa rser.jj. SQLUnit uses JEXL as the Apache Expression Commo parser and ns Apache License evaluator for JEXL its 1.0 ExpressionMa tcher matcher. Apache Commo ns Loggin g 1.0 JEXL needs Apache Logging as a runtime dependency.

https://javacc.dev.java.net Yes /

Yes

http://jakarta.apache.org/c Yes ommons/jexl/

Yes

Apache License

http://jakarta.apache.org/c Yes ommons/logging/

Yes

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.1 [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 Name Valid Values Description

If set to "true", SQLUnit will stop executing the current "true" or test file if there is a test failure or exception. If multiple haltOnFailure "false", default files are specified using nested fileset elements, then it "false" will continue to the next file.

Attribute Name

Valid Values

Description

debug

If set to "true", haltOnFailure will also be set to "true" "true" or regardless of the value set. Setting debug to "true" will "false", default output a very verbose trace log on the console. The trace "false" contains a line for every method entered while the test was running. If 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 If 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

logfile

A valid file name

logformat

Format name identifying a reporter

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). 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}

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. 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 nonCallable 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 nonCallable 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. 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. 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:

Input XML file XSL Transform file

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/dir

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:MyTestPro [java] Processing /home/sujit/src/sqlunit/test/mssqlserver/TestSQLUnit_SQLServer2KTypes.xml:TestSQLUnit_S ... [java] Processing /home/sujit/src/sqlunit/test/mock/variabletests.xml:variabletest [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. 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 sqlunitflat, 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.

Attributes None

Nested Elements Table 1. Nested Elements for sqlunit Name connection setup test Description Specifies one or more database Connections that need to be instantiated for the test to run. Specifies any setup tasks that need to happen before all the tests in the suite are run. Specifies a test that needs to run in this suite. Required Yes, one or more. No Either one or more of test, batchtest, diff, echo

Name

Description or func

Required

batchtest

Specifies a test that uses the JDBC batching functionality. 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 Either one or more of test, batchtest, diff, echo or func Either one or more of test, batchtest, diff, echo or func Either one or more of test, batchtest, diff, echo or func No

diff

echo

Echoes a string after substitution to the log. Populates a single value returned from a stored procedure or SQL call into the SymbolTable, identified by function name Specifies any tasks that need to happen after all the tests in the suite are run

func

teardown

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)*))

Parent Elements

sqlunit

Attributes None

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

Parent Elements test

Attributes Table 3. Attributes for skip Name Description Required

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

Nested Elements None

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)

Parent Elements sub

Attributes Table 4. Attributes for subparam Name id Description Specifies the sequence number of the parameter to be replaced. The sequence is one-based to keep parity with the JDBC specification. Required Yes

Specifies the name of the parameter to be replaced. This has been name 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 Yes 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. If set to true, indicates that this parameter is an SQL NULL value. Valid values are true or false. No, default is false.

isnull

inout

No, Specifies whether the parameter is an IN, OUT or INOUT parameter. default is Valid values are in, out and inout respectively. in.

Nested Elements None

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)*)

Parent Elements batchtest

Attributes Table 5. Attributes for batchresult Name expectedcount Description Specifies the number of rows expected to be updated as a result of executing the batchsql or batchcall element. If the expectedcount is specified, then the number of updatecount elements will not be counted for determining the test's success Specifies which statement in the batch is expected to fail when running the test. The index is zero-based Required

No

failed-at

No

Nested Elements

Table 6. Nested Elements for batchresult Name Description Required Yes, unless either or both expected-count and failed-at are specified

Specifies in the body of the element the number of rows expected to be modified by the SQL at updatecount the corresponding position (zero-based) in the batch

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)*)

Parent Elements test diff foreach setup teardown

Attributes Table 7. Attributes for call Name id Description An internal id for the stored procedure call Required No No

Used to specify a particular connection when multiple connectionConnections are specified in a given test. If not specified, id SQLUnit will try to use the default Connection, if one exists

Nested Elements Table 8. Nested Elements for call Name stmt param Description Required

Specifies the actual stored procedure call in JDBC format described Yes above in the body of this element 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*)?

Parent Elements setup prepare

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

name

value

static

class

Specifies the class name whose method has to be invoked to populate the variable

Name

Description

Required Only required if the class attribute is supplied

method Specifies the method name in the specified class to invoke.

Nested Elements Table 10. Nested Elements for set Name Description Specifies a SQL statement to invoke which will populate the variables in the result. Required Only required if there is no value attribute in the set tag. A call tag can also be specified here instead.

sql

call

Only required if there is no Specifies a stored procedure call to value attribute in the set tag. invoke which will populate the variables A sql tag can also be in the result. specified instead. 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.

result

Specifies arguments to a constructor of a Only required if we want to constructorArgs non-static method if that is being used to invoke a non-static method populate the variable to set. 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)?)

Parent Elements test

Attributes

None

Nested Elements None

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)*)

Parent Elements connection dynamicsql methodinvoker

Attributes None

Nested Elements Table 11. Nested Elements for jndi Name arg Description A tag that specifies the name and value of a single JNDI property. Required 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)*)

Parent Elements test

Attributes Table 12. Attributes for methodinvoker Name static class Description If set to true, specifies that the method being invoked is static. Valid values are true and false. Specifies the full class name of the class in which the method is defined. Required No, default is false. Yes Yes

method Specifies the method name to invoke.

Nested Elements Table 13. Nested Elements for methodinvoker Name Description Required

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

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)*)

Parent Elements sqlunit

Attributes None

Nested Elements Table 14. Nested Elements for teardown Name sql Description Specifies the SQL statements to be executed, with or without replaceable parameters, that should executed on teardown. Required No

Name foreach include

Description Contains bulk SQL statements such as SQL DELETE statements, which will be executed from within a foreach tag. Specifies an external file which contains the SQL statements to be executed on teardown.

Required No 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)*)

Parent Elements setup prepare teardown

Attributes

Table 15. Attributes for foreach Name Description Required

The name of the parameter that will be substituted in the SQL statement. For example, if the param is id, param Yes then all occurences of ${id} will be substituted by the current value of id. 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 semivalues colons. They are referred to later in the scope of the foreach tag as value_of_param.index. The index is 0based. Specifies the starting value of the sequence of values for the param No, either this or (start,stop,(step)) or (start,count,(step)) may be specified No, values can be specified instead. If neither is specified, then start defaults to 0. No, count can be specified instead. No, defaults to 1. No, stop can be specified instead.

start

stop step count

The ending value of the sequence of values for param The increment if a sequence is specified with start and stop The number of entries in the sequence specified by start and stop.

Nested Elements Table 16. Nested Elements for foreach Name sql Description 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}. Required 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))?)

Parent Elements sqlunit

Attributes Table 17. Attributes for connection Name Description Required No, default Connection is used if not supplied

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

Name id attribute defined.

Description

Required

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 No 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.

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 transaction- enforce boundaries of the unit of work by putting COMMIT No, defaults support and ROLLBACK in the SQL or stored procedures. Finally, to on 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. reconnecton-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 No, defaults to the database server name

If specified, will override the database server name that is derived from the DatabaseMetaData for the connection. server-name This is used to lookup data types supported by this database.

Nested Elements Table 18. Nested Elements for connection Name driver url user Description Specifies the full Java class name for the JDBC driver. This is needed for building the Connection using JDBC. Specifies the JDBC URL to the database Specifies the database user who will connect to the database Required Yes (JDBC) Yes (JDBC) Yes

Name

Description

Required (JDBC)

password jarfile-url

Specifies the password for the database user Specifies a URL (eg. file:///tmp/mydriver.jar) for a JAR file containing a JDBC driver which is not in the CLASSPATH.

Yes (JDBC) No (JDBC)

Specifies the name of a DataSource. By convention, this would look something like jdbc:/myDSN, where the DataSource object datasource 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 Yes name-value pairs locate the JNDI service. Each arg element will (JNDI) correspond to a line in the jndi.properties file for the given JNDI server.

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)

Parent Elements sqlunit foreach setup test diff teardown

Attributes Table 19. Attributes for func Name Description 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. The name of the funcdef element to look up. Required

name

Yes

lookup

Yes No

connection- The id of the connection to use, when multiple connections are id defined. If not specified, it will use the default connection.

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 No quotes to indicate that. This type of query has no indication of the type of parameter being passed to it. The name or number of the sql call No

id

Nested Elements

Table 20. Nested Elements for func Name skip Description Indicates whether the func should be skipped or not. Required No No

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

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

Parent Elements sqlunit

Attributes Table 21. Attributes for echo Name Description Required Yes, required by the SQLUnit logger Yes no

name A name for this echo tag text The text to echo

Specifies the expected value of text, if used in a test context. value Saves caller the trouble of having to manually compare the expected and actual returned values.

Nested Elements None

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)

Parent Elements setup teardown

Attributes Table 22. Attributes for include Name file Description Specifies a relative or absolute path name to the file containing the SQL statements. Required Yes

When multiple Connections are defined in a SQLUnit test, this connection- attribute is used to select the Connection to use for running the id SQL statements. If not specified, SQLUnit will use the default Connection if that is specified.

No

Nested Elements None

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))?)

Parent Elements test

Attributes Table 23. Attributes for result Name id The id of the result 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. Description Required No No, defaults to false

echo

Nested Elements Table 24. Nested Elements for result Name Description 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. Specifies the updatecount that is returned from the SQL or stored procedure call. Required

outparam

No

updatecount

No.

resultset

The resultset tag specifies a single resultset returned from No. a database call (SQL or stored procedure). A result element can contain one or more resultsets, which in turn

Name

Description will contain one or more rows, which would contain one or more columns. If there are no rows, a resultset can also be empty.

Required

exception

No. Exception The exception tag is used to test for expected failures of a tags can coexist stored procedure in response to specific inputs. 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)*)

Parent Elements methodinvoker dynamicsql

Attributes None

Nested Elements Table 25. Nested Elements for methodArgs Name arg Description A tag that specifies the name, type and value of the method argument. Required 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)+

Parent Elements resultset

Attributes Table 26. Attributes for row Name id The row id Description Required Yes No, defaults to false

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

Nested Elements Table 27. Nested Elements for row

Name col

Description

Required

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)

Parent Elements call sql paramset subdef

Attributes Table 28. Attributes for param Name id Description Specifies the sequence number of the parameter to be replaced. Required Yes

Name

Description The sequence is one-based to keep parity with the JDBC specification.

Required

name

Specifies the name of the parameter to be replaced. This has been No added for readability to help stored procedure authors/testers to spot errors quickly. 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 Yes 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. Specifies the scale of the parameter value if the value maps to a BigDecimalType. This is an optional parameter. SQLUnit will try No its best to guess the scale based on the actual value, but this is useful if the value cannot be specified or is NULL. Specifies the name of a user-defined type. This is an optional parameter. No

type

scale

typename

is-null

No, If set to true, indicates that this parameter is an SQL NULL value. default is Valid values are true or false. false. Specifies whether the parameter is an IN, OUT or INOUT parameter. Valid values are in, out and inout respectively. No, default is in.

inout

Nested Elements None

Examples A simple param tag definition


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

The funcdef Tag

Description

Syntax
funcdef ::=

Parent Elements setup

Attributes Table 29. Attributes for funcdef Name name query Description The name of the function definition. This will be used to call the definition from the func tag. The SQL query with numbered replaceable parameters (see example below) which will be called to return the result. Required Yes Yes No

description User Documentation for the function definition.

Nested Elements None

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)+)

Parent Elements batchtest

Attributes Table 30. Attributes for batchsql Name Description Required

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

No

Nested Elements Table 31. Nested Elements for batchsql Name stmt Description Contains the SQL statement or stored procedure call in the body of the element Required 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)*)

Parent Elements

test

Attributes None

Nested Elements Table 32. Nested Elements for prepare Name set sql foreach Description This tag sets values for SQLUnit test variables into the SQLUnit symbol table. Identifies one or more SQL statements that should be executed as part of the per-test prepare Required No. No.

Specifies a block within a foreach block which is executed as part of No. the prepare.

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

Parent Elements classifiers

Attributes None

Nested Elements None

The paramset Tag

Description Wrapper tag for one or more param elements.

Syntax
paramset ::= ((param)+)

Parent Elements batchcall

Attributes Table 33. Attributes for paramset Name id Description Required

Specifies the id of the paramset element. Yes

Nested Elements Table 34. Nested Elements for paramset Name param Description Specifies argument names and values to be passed into a batchcall call. Required 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)*)

Parent Elements setup

Attributes Table 35. Attributes for subdef Name name query description type Description The name of the subroutine definition. This will be used by the sub tag to specify the definition to look up. The SQL or stored procedure call with replaceable parameters specified as in the sql or call tags. Required Yes Yes

A user-friendly description of the subroutine definition. Used No for documentation 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 36. Nested Elements for subdef Name Description Required

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

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)

Parent Elements row

Attributes Table 37. Attributes for col Name id The column id Description Required Yes No

name The column name type

The XML name of the data type for the column Yes

Nested Elements

None

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)

Parent Elements sqlunit

Attributes Table 38. Attributes for batchtest Name name Description The name of the batch test. THis is used by Yes Required

Name

Description SQLUnit to print progress messages to its log output

Required

assert failuremessage java-objectsupport

Specifies a single or comma-separated assertion that must hold true for the batch test. If specified, SQLUnit will print the user-supplied failure message if the test failed If set to true, Java Object Support is enabled for the test

No, defaults to equal if not specified. No, default is no failure message No, default is false

Nested Elements Table 39. Nested Elements for batchtest Name skip Description Indicates whether the func should be skipped or not. 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. Specifies the set of batch calls to be made. Either batchcall or batchsql must be specified Specifies the set of batch calls to be made. Either batchcall or batchsql must be specified Required No

classifiers

No

batchcall batchsql

No, but see Description No, but see Description Yes

batchresult Specifies the expected return values from the call

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)*)

Parent Elements dynamicsql methodinvoker

Attributes None

Nested Elements Table 40. Nested Elements for constructorArgs Name arg Description Required

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)+)

Parent Elements batchtest

Attributes Table 41. Attributes for batchcall Name Description Required

connection- When multiple Connections are defined for a SQLUnit test, this No

Name id

Description 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.

Required

Nested Elements Table 42. Nested Elements for batchcall Name stmt Description Contains the SQL statement or stored procedure to call in the body. Yes At least one needs to be provided Required

param Specifies a set of param elements

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)

Parent Elements result

Attributes Table 43. Attributes for outparam Name id name type Description The param-id of the outparam parameter. Required Yes

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

Nested Elements Table 44. Nested Elements for outparam Name Description Required

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

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 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)?)

Parent Elements sqlunit

Attributes Table 45. Attributes for test

Name name

Description Specifies a human-readable name for the test, which will be printed to the log as part Yes of SQLUnit's progress messages. Specifies a single or comma-separated list of assertions that must be true for the test. Allows the caller to supply an error message which should be displayed if the test failed. If set to on, indicates that Java Object Support is turned on. Valid values are on and off.

Required

assert failuremessage java-objectsupport

No, defaults to equal No, if not specified, no user message will be displayed.

No, default is off

expecteddurationmillis

If specified, specifies that SQLUnit should time the test and fail the test if the test does not complete in the window specified +/- a No percentage tolerance if specified, or 10% if not. 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 percentagetolerance is not specified.

percentagetolerance

Nested Elements Table 46. Nested Elements for test Name skip Description Indicates whether the test should be skipped No or not. Allows user to provide classification criteria for the test which SQLUnit can use to decide whether it should run the test or not No based on criteria provided to match the classifier. Specifies zero or match elements that should be applied to match the result returned with that specified. Specifies SQL setup code that must be run on a per-test basis. No Required

classifiers

match

prepare

No

Name sql

Description Specifies the SQL statement that must be run for this test. Specifies a stored procedure that must be run for the test.

Required Either one of sql, call, methodinvoker, dynamicsql or sub Either one of sql, call, methodinvoker, dynamicsql or sub Either one of sql, call, methodinvoker, dynamicsql or sub

call

Specifies a method that should be invoked methodinvoker for the test.

dynamicsql

Specifies a method which returns a String of Either one of sql, call, dynamic SQL code that should be executed methodinvoker, for this test. dynamicsql or sub Specifies a predefined and partially specified named SQL or stored procedure call Specifies the expected result from the test. Either one of sql, call, methodinvoker, dynamicsql or sub func is required. Yes

sub

result

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)*

Parent Elements result

Attributes Table 47. Attributes for resultset Name id partial The resultset id 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. Description Required Yes No, defaults to false No

Specifies the number of rows in the resultset. This is specified rowcount when the test client wants SQLUnit to only match the number of rows in the resultset, rather than the contents themselves. 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. order-by 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 48. Nested Elements for resultset

Name row

Description

Required

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)*)

Parent Elements diff

Attributes Table 49. Attributes for match Name Description Required

Name

Description

Required

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

col-id

matcher

Nested Elements Table 50. Nested Elements for match Name arg Description 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. Required 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 sub Tag

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

Syntax
sub ::=

Parent Elements test

Attributes Table 51. Attributes for sub Name lookup connectionid Description Specifies the name of the subdef element to look up. The connection-id to use or use the default connection if not specified. Required Yes No

Nested Elements Table 52. Nested Elements for sub Name Description Required Zero or more

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

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)?

Parent Elements result

Attributes None

Nested Elements Table 53. Nested Elements for exception Name code Description The error code for the expected exception Required 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))

Parent Elements test

Attributes Table 54. Attributes for diff Name Description Required

Name name assert failuremessage java-objectsupport multithreaded

Description The name of the test used by SQLUnit to report progress messages in its log output. Specifies a single assertion or a comma-separated list of assertions which must be true for this diff. If specified, SQLUnit will print the user-supplied failure message if the test failed. Yes

Required

No, defaults to equal if not specified No

If specified, Java Object Support is enabled for the No, default is false test If set to true, runs the calls in parallel to save time No, default is false (serial execution)

Nested Elements Table 55. Nested Elements for diff Name skip Description Indicates whether the diff should be skipped or not. No Required

Allows user to provide classification criteria for the diff which SQLUnit can use to decide classifiers whether it should run the diff or not based on criteria provided to match the classifier. match prepare

No

Provides overrides for the matching strategy on a No per column basis Allows per test setup calls No. Zero or one prepares can be specified. Either one of sql or call needs to be specified. Two and only two needs to be specified Either one of sql or call needs to be specified. Two and only two needs to be specified.

sql

Specifies the SQL statement to execute.

call

Specifies the stored procedure to execute.

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)

Parent Elements classifiers

Attributes None

Nested Elements None

The arg Tag

Description Carries argument values and types for the containing element

Syntax
arg ::= (EMPTY)

Parent Elements jndi match constructorArgs methodArgs

Attributes Table 56. Attributes for arg Name name Description The name of the argument to be used Yes Yes No, but required in methodArgs and constructorArgs Required

value The value of the argument type The type of the argument

Nested Elements None

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)*)

Parent Elements

foreach setup test diff teardown

Attributes Table 57. Attributes for sql Name id Description The name or number of the sql call Required No No

When multiple Connections are defined in a SQLUnit test, this connectionattribute refers to the Connection to use for running this call. If id not specified, SQLUnit tries to look up the default Connection.

Nested Elements Table 58. Nested Elements for sql Name stmt Description Specifies the actual SQL statement, with or without replaceable parameters in the body of the tag. Occurs once for each replaceable parameter, if specified, for the SQL string. Yes Yes, if there are replaceable parameters in the SQL string. Required

param

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)*)

Parent Elements test foreach setup diff teardown

Attributes Table 59. Attributes for dynamicsql Name static Description Required

No, default is If set to true, specifies that the method is a static method false (non-static method) Specifies the full class name where the method is defined Specifies the method name to invoke The name or number of the sql call Yes Yes No

class method id

When multiple Connections are defined in a SQLUnit connection- test, this attribute refers to the Connection to use for id running this call. If not specified, SQLUnit tries to look up the default Connection.

No

Nested Elements Table 60. Nested Elements for dynamicsql Name Description Required

Name

Description

Required

Wraps a set of arg elements which Not required if this is a static constructorArgs specify the arguments the constructor method, or if the class has a for the class will take 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. 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.

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 61. SQLUnit Code Layout Package Name Location Description The main package which contains the classes which did not fit neatly into the subpackages detailed below. This used to be the only package for SQLUnit. Contains the Ant SQLUnit task Contains beans to support the handlers. Older versions of SQLUnit handlers would return an Object from the process() method. They still return Objects, but now some of the handlers return well-defined Objects which can be cast to one of the beans in this package.

net.sourceforge.sqlunit

src/net/sourceforge/sqlunit

net.sourceforge.sqlunit.ant

src/net/sourceforge/sqlunit/ant

net.sourceforge.sqlunit.beans

src/net/sourceforge/sqlunit/beans

net.sourceforge.sqlunit.handlers

Contains all the src/net/sourceforge/sqlunit/handlers handler implementations.

Package Name

Location

Description All the implementations implement the IHandler interface. Contains implementations of various types. All the implementations implement the IType interface.

net.sourceforge.sqlunit.types

src/net/sourceforge/sqlunit/types

Contains some user-defined matcher classes for use with the net.sourceforge.sqlunit.matchers src/net/sourceforge/sqlunit/matchers diff tag. All matchers implement the IMatcher interface. Contains reporter classes that can be used with SQLUnit. net.sourceforge.sqlunit.reporters src/net/sourceforge/sqlunit/reporters All reporters implement the IReporter interface. Contains utility classes whose methods get called by SQLUnit. Contains tools to generate test cases from existing SQL statements using console input and GUI based input.

net.sourceforge.sqlunit.utils

src/net/sourceforge/sqlunit/utils

net.sourceforge.sqlunit.tools

src/net/sourceforge/sqlunit/tools

Package Name net.sourceforge.sqlunit.test test/java

Location

Description Contains some Java code used for LOB testing. Contains code for the mock database framework for testing SQLUnit code independent of a database. Contains subdirectories named after various databases and contains stored procedures and test XML files for these databases. Most of SQLUnit is interface driven, and uses Factory classes to instantiate a named implementation of an interface. The registry files contain the mappings to the various implementations.

net.sourceforge.sqlunit.test.mock test/java/mock

Test files for various databases

test/*

SQLUnit registry files

etc/*.properties

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 <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/log4j1.2.8.jar,file:lib/commons-lang.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-jdbcconnection.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

TransactionSupport on (default) off implicit

AutoCommit Set? No No Yes

COMMIT on success? Yes No No

ROLLBACK on failure? Yes No No

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 transactionsupport=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. 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 Message Corrective 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

Error Message Test File {filename} not found. {usage}

Corrective Action The test file specified could not be found by SQLUnit. Check to see if the test file exists as specified on your operating system The test file name was specified both in the testfile attribute as well as the fileset nested element. Use one or the other, not both SQLUnit does not have sufficient permissions to read the specified test file. Fix the permissions on the file at the operating system level SQLUnit could not build the Connection object with the properties supplied. Verify that the properties for building the Connection are valid. SQLUnit 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. The 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. This 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. This 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. 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

Input Test File specified twice, specify either file or fileset

Cannot read Test File {filename}

Cannot build Connection (id={id}) because {reason}

{resource} not found in specified Context

Included file {filename} not found

System Error: Element {element} is null

System Error: Connection {id} is null or incorrectly specified

Match Exception in class: {classname}, message: {exception_message}

Error Message

Corrective Action more information as to what the problem is. Usually, it can be fixed by passign arguments correctly to the Matcher class. The values for the resultset-id, row-id and col-id attributes to the Match tag can either be specified as exact numbers, a commaseparated 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. Some 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. 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 userdefined 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 at the specified position could not be done because of the reason specified. 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.

Cannot parse match pattern: {pattern}

Matcher class {className} not available in CLASSPATH

No match on variable at [rset,row,col]=[ {rset},{row},{col}] using class {matcher_class_name} expected: {expected_result} but got {actual_result}

Partial Match impossible at {position}: {reason}

At {position}, could not convert {actual_value} to {java_class_name} for {SQL_Type_Name}({SQL_Type})

Error Message

Corrective Action

The Object returned from the database is not of the same class that the class mapped to the At {position}, could not convert SQL Type SQL_Type wraps. You will most {java_class_name} returned from database likely need to provide an override mapping to {SQL_Type_Name}({SQL_Type}) for your database, or, if there is no suitable mapping, you/we could write a mapping class to handle this type. The 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. SQLUnit could not find a mapping type for this specified type name. This is a nonstandard (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. 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 named symbol could not be found. Check your logic and verify that you have set the symbol before trying to use it. The named parameter appears in the subdef as unspecified, but was not specified in the corresponding sub. 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.

At {position}, datatype {SQL_Type_Name}({SQL_Type}) is not supported

No type mapping found for (server.)type {{server_name}.{datatype}}

Parsing of {text} failed because {reason}

The symbol could not be found in the symbol table Parameter {parameter_name} is not defined Statement {statement} ({number}) in include file {filename} returned result {error_code}

Variable {variable_name} is invalid, SQLUnit only accepts variables in the should be in the format \${variable_name} format ${variable_name}. Fix the name in

Error Message

Corrective Action 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. SQLUnit expects a numeric value at the location indicated, but parsing it from its String value to the appropriate numeric value resulted in a NumberFormatException. 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. 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. The assertion provided in the assert attribute for this test element is not supported. Please check the documentation for a list of supported assertions. The specified assertion failed. The 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

Value {supplied_value} for {variable} could not be converted to a numeric value

Could not convert to digest because {reason}

Cannot invoke method {className}.{methodName}

Assertion {assert} is not supported

Assertion {assert} failed ({reason}), expected {expected_result}, but got {actual_result}, {failure_message} Expected to complete in {expectedduration} ms +/- {percent-tolerance}%, but took {actual_duration}ms

Error Message

Corrective Action 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: This message is reported at the end of each {total_tests_run}, failures: SQLUnit test file if there was an error or {number_of_failed_tests}, errors: failure running the tests in the file. {number_of_errors_encountered} This message is reported at the end of an SQLUnit run, spanning one or more test One or more SQLUnit Tests failed, see the files, if there was a failure or an error in any {console|logfile} for details one of the tests. The output will describe the exact nature of the failure(s). 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.

{Type} error ({Exception_Class}): {message}

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 Allo Allow Is ws s Server Sortab Inpu Outp le t ut Micros oft SQL Server Any

Type Class

Type Name

Wrapper for

TextType

NTEXT

No

Yes

No

java.io.InputStrea m java.sql.Clob java.sql.Clob java.sql.Array java.lang.Object n/a n/a n/a n/a n/a java.lang.Integer java.lang.Integer java.io.InputStrea m

ClobType ClobType ArrayType

CLOB

No No no No No No No No No Yes Yes No

Yes Yes yes Yes No No No No No Yes Yes Yes

No No no No No No No No No Yes Yes No

LONGVARCHA MySQ R L ARRAY Any Any Any Any Any Any Any Any Sybase ASA Any

JavaObjectType JAVA_OBJECT UnsupportedTy DATALINK pe UnsupportedTy DISTINCT pe UnsupportedTy NULL pe UnsupportedTy REF pe UnsupportedTy STRUCT pe IntegerType IntegerType BinaryType INTEGER SMALLINT BINARY

Type Class

Type Name

Allo Allow Is ws s Server Sortab Inpu Outp le t ut Any Any Any Oracle Any Any Any Any Any Any Any No Yes Yes No Yes Yes No No Yes Yes no Yes Any Any Any Any Any Any No Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes yes Yes Yes Yes Yes Yes Yes Yes Yes Yes No Yes Yes No Yes Yes No No Yes Yes no Yes No Yes Yes Yes Yes Yes Yes Yes

Wrapper for

BinaryType ByteType TimeType

LONGVARBIN ARY TINYINT TIME

java.io.InputStrea m java.lang.Byte java.sql.Time java.sql.ResultSet java.sql.Date java.lang.Long byte[] byte[] java.math.BigDec imal java.math.BigDec imal java.sql.Blob java.lang.Short java.lang.Object java.lang.Double java.sql.Timestam p java.lang.Boolean java.lang.Boolean java.lang.String java.lang.String java.lang.String

OracleCursorTy CURSOR pe DateType LongType DATE BIGINT

ByteArrayType BINARY ByteArrayType VARBINARY BigDecimalTyp DECIMAL e BigDecimalTyp NUMERIC e BlobType ShortType OtherType DoubleType TimestampTyp e BooleanType BooleanType StringType StringType StringType BLOB SMALLINT OTHER DOUBLE TIMESTAMP BIT BOOLEAN CHAR

LONGVARCHA Any R VARCHAR Any Micros oft SQL Server

StringType

NCHAR

Yes

Yes

Yes

java.lang.String

Type Class

Type Name

Allo Allow Is ws s Server Sortab Inpu Outp le t ut Micros oft SQL Server Oracle Any

Wrapper for

StringType

NVARCHAR

Yes

Yes

Yes

java.lang.String

StringType FloatType

FIXED_CHAR REAL

Yes Yes

Yes Yes

Yes Yes

java.lang.String java.lang.Float

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 Type FLOAT Old Mapping FloatType (java.lang.Float) DoubleType (java.lang.Double) New Mapping DoubleType (java.lang.Double) BigDecimalType (java.math.BigDecimal )

NUMERIC

VARBINAR Y

BinaryType ByteArrayType (java.io.InputStream (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-String none Description Asserts nothing about the generated and the actual results. This is typically used to disable assertions for the particular test. Asserts 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. Used In diff,test

equal

diff,test

not-equal exceptionmatches not-exception numberoutparamsequal outparamsequal update-countsequal

Asserts that the two results are not equal. This is an inversion diff,test of the equals assertion. Asserts that the two results have identical exceptions. Asserts that the result returned is NOT an exception Asserts that the two results have the same number of outparam elements. Asserts that the outparams in both the results are equal. Asserts that the update counts in the two results are equal. diff,test diff,test diff,test

diff,test diff,test diff,test diff,test diff,test

number-ofAsserts that the number of resultsets in the two results are resultsets-equal equal. resultsets-equal resultsetvalues-equal resultset-typesequal Asserts that the resultsets are equal, or match if the test contains embedded match tags. Asserts that the columns in the specified and actual resultset have the same values. No assertion is made for types.

Asserts that the columns in the specified and actual resultsets diff,test are of the same type.

fail-with-failure Asserts that the test will fail with the specified error message. diff,test none Asserts nothing about the generated and the actual batch results. This is typically used to disable assertions for the particular test. Asserts 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. diff,test

equal

batchtest

Assert-String not-equal

Description Asserts that the two batchresults are not equal. This is the inverse of the equal assertion. Asserts 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.

Used In batchtest

failed-at-equal

batchtest

Asserts that the number of update counts specified in the expected-count- expected batchresult, either as an expected-count attribute, or batchtest equal listed explicitly, is the same as the one in the generated batchresult. updatecountequal fail-with-failure Asserts that the updatecount values are the same for the result specified by batchresult and the result generated by the batchtest call to batchsql or batchcall. Asserts 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

Pattern

Description 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. Specifying a single number for a given attribute means that SQLUnit will match all corresponding elements having this attribute in the result element. Specifying a range of numbers for a given attribute means that SQLUnit will match all corresponding elements whose attribute falls in the specified range. 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.

Example resultset-id="1" row-id="*" col-id="*" means match all rows and all columns in resultset 1

Single number (n)

resultset-id="*" row-id="*" col-id="3" will match the third column in all rows in all resultsets 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. resultset-id="*" row-id="1" col-id="1,2,3" will do the same thing as the above example. 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.

Range of numbers (m-n)

Enumeration of numbers (m,n,p)

Combination of Enumeration and Range (m-n,p,q)

Any combination of range patterns and simple enumeration separated by commas is also supported.

List of currently supported Matchers


Table 2. SQLUnit Matchers Matcher Name Author Description Argume nts match : Always returns true if

AllOrNothing Matcher

The AllOrNothingMatcher is an Sujit Pal implementation of the IMatcher (spal@users.sourc interface used to define rulesets for eforge.net) matching columns in SQLUnit. This

Matcher Name

Author

Description 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. 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.match ers.SignificantDigitsMatcher"> <arg name="signif-digits" value="3" /> </match>

Argume nts set to true, else always returns false.

Tim Cull SignificantDigit (trcull@yahoo.co sMatcher m)

signifdigits : Number of significa nt digits to match to

The TypelessMatcher matches column values only from two different SQL or stored procedure calls. This would be useful when the data returned from two Sujit Pal TypelessMatch different databases may have the same (spal@users.sourc None : er String value but the actual datatypes eforge.net) they are implemented as may be different. This is the matcher that is used implicitly when the assertion "resultsetvalues-equal" is called. The RangeMatcher is an implementation of the IMatcher interface used to define Sujit Pal rulesets for matching columns in (spal@users.sourc SQLUnit. This matcher will accept an eforge.net) absolute tolerance value and check to see that the target is within (+/-) tolerance of the source. toleranc e : An absolute toleranc e value.

RangeMatcher

Matcher Name

Author

Description

Argume nts pctoleranc e:A percenta ge toleranc e value. pctoleranc e:A percenta ge toleranc e value.

PercentageRan geMatcher

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

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

ForgivingNull Matcher

Tim Cull (trcull@yahoo.co m)

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 None : 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.match ers.ForgivingNullMatcher"> </match>

Matcher Name

Author

Description

Argume nts expressi on : A valid JEXL expressi on. Source and target variables in the expressi on are referenc ed by the pseudovariables expected .value and actual.va lue respectiv ely.

ExpressionMat cher

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

Extending SQLUnit: Writing your own Matcher


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.99 2005/07/08 05:02:15 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 021111307, USA. */ package net.sourceforge.sqlunit.matchers; (3) import import import import java.util.Map; (4) net.sourceforge.sqlunit.IErrorCodes; net.sourceforge.sqlunit.IMatcher; 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.99 $ */ 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. 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 Name TextReporter Author Rob Nielsen (robn@asert.com.au) Description SQLUnit reporter that writes its text to standard output. This is the default reporter for SQLUnit. SQLUnit Reporter that works with the Canoo Web Test framework. Generates XML that can be converted to output for the Canoo Web Test console.

CanooWebTestReporter

Rob Nielsen (robn@asert.com.au)

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>

S-ar putea să vă placă și