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

SQLUnit User Guide

Embed Size (px)

DESCRIPTION

Unit testing tool for SQL procedures

Citation preview

Page 1: SQLUnit User Guide

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

Page 2: SQLUnit User Guide

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

Page 3: SQLUnit User Guide

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

Page 4: SQLUnit User Guide

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

Page 5: SQLUnit User Guide

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.

Page 6: SQLUnit User Guide

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

Page 7: SQLUnit User Guide

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

PostgreSQL 7.3 Sujit Pal

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 3.4 Sujit Pal

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.

Oracle

Sahib Singh Wadhwa, Andrei Grigoriev, Sujit Pal

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.

Sybase ASE 12.5

Sowmya Shetty, Lakshmi, Sujit Pal

Complete, with lots of testing support from Soumya Shetty and Lakshmi

IBM DB2 Sujit Pal

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.

HSQLDB (formerly Hypersonic)

Campbell Boucher-Burnett

In progress

Informix SE 7.0 -

Not started. This should work, but has not been tested. The test cases are available in CVS, but the JDBC driver that I had available had issues with working with PAM authentication on Linux. If someone has a

Page 8: SQLUnit User Guide

Database Name

(Version) Porter Status

working setup of Informix, then I would appreciate you running the tests and letting me know the results.

Microsoft SQL Server 2000

James Henderson, Paul King

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.

Sybase ASA 9.0.1 David Fishburn Complete, test suite included with distribution

(test/asa/README)

License Information English is broken, it does not distinguish between free beer and free

speech. Spanish on the other hand, distinguishes between gratis and libre. Free software is libre software.

--Miguel de Icaza

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

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

Installing

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

Page 9: SQLUnit User Guide

$ export CVSROOT=:pserver:[email protected]:/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

Software and Versio

n

How it is used Licensing URL Require

d Included

Java 1.4 or later

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

Proprietary but free for download and use

http://java.sun.com/ Yes No

Ant 1.6 or later

Ant is used to build the SQLUnit package. An ant task is provided to run SQLUnit from within Ant.

Apache License http://ant.apache.org/

Yes, although you can also just call it from the command line with

No

Page 10: SQLUnit User Guide

Software and Versio

n

How it is used Licensing URL Require

d Included

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/index.htm Yes Yes

JDOM Beta 9 or later

SQLUnit uses JDOM to do its XML parsing and rendering.

Apache License http://www.jdom.org/ Yes Yes

Apache Log4J 1.2.8 or higher

This is used for logging in SQLUnit.

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

Apache Xerces-J 1.4.4

Used by Canoo WebTest

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

MockRunner 0.2.7

SQLUnit uses this to set up and run a set of Mock tests against a Mock database that consists of method calls which are introspected.

Apache style license

http://mockrunner.sourceforge.net/

No. This is only needed if you want to run the mock test suite to make sure everything worked.

Yes

Apache Commons-Lang

Used by MockRunner Apache License http://jakarta.apache.org/c

ommons/ No, see above. Yes

XDoclet 1.2.1

SQLUnit uses XDoclet to BSD License http://xdoclet.sourceforge.

net/ No, only needed No

Page 11: SQLUnit User Guide

Software and Versio

n

How it is used Licensing URL Require

d Included

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

to generate the documentation.

Apache Jalopy 0.6.2

SQLUnit uses Jalopy to provide a guide for coding conventions used in SQLUnit. Contributed code typically passes through Jalopy to get reformatted before inclusion.

BSD License http://jalopy.sourceforge.net/

No, only needed to reformat contributed code before submission.

No

Apache Checkstyle 3.4

SQLUnit uses CheckStyle to audit code style on an ongoing basis. The checks that have been configured are a union of the sun_checks.xml and BlochEffectiveJava.xml files supplied with the distribution.

LGPL http://checkstyle.sourceforge.net/

Yes, if you plan on making code contributions.

No

Page 12: SQLUnit User Guide

Software and Versio

n

How it is used Licensing URL Require

d Included

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

JavaCC 3.2

SQLUnit uses JavaCC to generate a parser for include files based on the grammar specified in the IncludeFileParser.jj.

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

Apache Commons JEXL 1.0

SQLUnit uses JEXL as the Expression parser and evaluator for its ExpressionMatcher matcher.

Apache License http://jakarta.apache.org/commons/jexl/ Yes Yes

Apache Commons Logging 1.0

JEXL needs Apache Logging as a runtime dependency.

Apache License http://jakarta.apache.org/commons/logging/ Yes 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.

Page 13: SQLUnit User Guide

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

Page 14: SQLUnit User Guide

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

Page 15: SQLUnit User Guide

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

haltOnFailure "true" or "false", default "false"

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

Page 16: SQLUnit User Guide

Attribute Name Valid Values Description

debug "true" or "false", default "false"

If set to "true", haltOnFailure will also be set to "true" regardless of the value set. Setting debug to "true" will output a very verbose trace log on the console. The trace contains a line for every method entered while the test was running.

logfile A valid file name

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

logformat Format name identifying a reporter

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

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

Page 17: SQLUnit User Guide

widely used.

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

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

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

Page 18: SQLUnit User Guide

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

Page 19: SQLUnit User Guide

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.

Page 20: SQLUnit User Guide

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

Page 21: SQLUnit User Guide

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>

Page 22: SQLUnit User Guide

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

Page 23: SQLUnit User Guide

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:

Page 24: SQLUnit User Guide

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:

Page 25: SQLUnit User Guide

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:

Page 26: SQLUnit User Guide

compile: [javac] Compiling 10 source files to /home/sujit/src/sqlunit/build compile-test: [javac] Compiling 31 source files to /home/sujit/src/sqlunit/build def: test: init: precompile: compile: [javac] Compiling 10 source files to /home/sujit/src/sqlunit/build canoo2html: [xslt] Processing /home/sujit/src/sqlunit/output/mytests.xml to /home/sujit/src/sqlunit/output/mytests.xml.html [xslt] Loading stylesheet /home/sujit/src/sqlunit/etc/canoo2html_result_transform.xsl BUILD SUCCESSFUL Total time: 10 seconds

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

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

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

SQLUnit Tags

List of currently supported tags

Page 27: SQLUnit User Guide

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

connection Specifies one or more database Connections that need to be instantiated for the test to run. Yes, one or more.

setup Specifies any setup tasks that need to happen before all the tests in the suite are run. No

test Specifies a test that needs to run in this suite. Either one or more of test, batchtest, diff, echo

Page 28: SQLUnit User Guide

Name Description Required

or func

batchtest Specifies a test that uses the JDBC batching functionality.

Either one or more of test, batchtest, diff, echo or func

diff Specifies a test that compares the results generated by two SQL or stored procedure calls

Either one or more of test, batchtest, diff, echo or func

echo Echoes a string after substitution to the log. Either one or more of test, batchtest, diff, echo or func

func Populates a single value returned from a stored procedure or SQL call into the SymbolTable, identified by function name

Either one or more of test, batchtest, diff, echo or func

teardown Specifies any tasks that need to happen after all the tests in the suite are run No

The setup Tag

Description

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

Syntax

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

Parent Elements

Page 29: SQLUnit User Guide

sqlunit

Attributes

None

Nested Elements

Table 2. Nested Elements for setup

Name Description Required

set Sets initial values for SQLUnit variables. No

funcdef Defines a SQL or stored procedure call that can be used like a function call (single string result). No

subdef Defines the structure of a SQL or stored procedure call. Values of parameters can be supplied or overriden in the actual sub call. No

sql Specifies the SQL statement to be executed with or without replaceable parameters. No

foreach Contains bulk SQL statements, such as inserts which are executed within the foreach tag. No

include Specifies an external file which contains the SQL statements to execute during the setup phase. No

Examples

A simple setup to delete rows from a table

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

The skip Tag

Description

Page 30: SQLUnit User Guide

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)

Page 31: SQLUnit User Guide

Parent Elements

sub

Attributes

Table 4. Attributes for subparam

Name Description Required

id Specifies the sequence number of the parameter to be replaced. The sequence is one-based to keep parity with the JDBC specification. Yes

name Specifies the name of the parameter to be replaced. This has been added for readability to help stored procedure authors/testers to spot errors quickly.

No

type

Specifies the type of the parameter to be replaced. The type is a String, and has a one to one mapping to the datatypes defined in java.sql.Types. It also allows some additional Oracle specific types specified in net.sourceforge.sqlunit.OracleExtensionTypes. The value of the type is the same as the field name of the member variables in Types or OracleExtensionTypes classes. Thus if the type is Integer, the corresponding SQL type is java.sql.Type.INTEGER, and the string value that should be used here is INTEGER.

Yes

is-null

If set to true, indicates that this parameter is an SQL NULL value. Valid values are true or false.

No, default is false.

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

No, default is in.

Nested Elements

None

Examples

Page 32: SQLUnit User Guide

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

expected-count

Specifies the number of rows expected to be updated as a result of executing the batchsql or batchcall element. If the expected-count is specified, then the number of updatecount elements will not be counted for determining the test's success

No

failed-at Specifies which statement in the batch is expected to fail when running the test. The index is zero-based No

Nested Elements

Page 33: SQLUnit User Guide

Table 6. Nested Elements for batchresult

Name Description Required

updatecount

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

Yes, unless either or both expected-count and failed-at are specified

Examples

A typical batchresult element

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

The call Tag

Description

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

Syntax

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

Parent Elements

test diff foreach setup teardown

Page 34: SQLUnit User Guide

Attributes

Table 7. Attributes for call

Name Description Required

id An internal id for the stored procedure call No

connection-id

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

No

Nested Elements

Table 8. Nested Elements for call

Name Description Required

stmt Specifies the actual stored procedure call in JDBC format described above in the body of this element Yes

param A param element specifies a single replaceable parameter specified for the stored procedure call.

Zero or more

Examples

Call to Stored Procedure dept_id=AddDept(dept_name)

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

The set Tag

Description

Page 35: SQLUnit User Guide

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 Required

name

Specifies the name of the SQLUnit variable. A variable must always be specified like ${varname}. Attempts to specify variables like varname or $varname will result in syntax error.

Yes

value

Specifies the initial value of the variable. The value must be specified. In cases where you do not know the value, you can initialize it with a default value, or not invoke the set tag to declare the variable. When the variable is first encountered in SQLUnit, it will implicitly create an entry for that variable in the symbol table.

Not required if nested elements are specified.

static Specifies whether the method call is static. Valid values are true and false.

Required if the class and method attributes are also supplied

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

Only required if the method attribute is also supplied

Page 36: SQLUnit User Guide

Name Description Required

method Specifies the method name in the specified class to invoke. Only required if the class attribute is supplied

Nested Elements

Table 10. Nested Elements for set

Name Description Required

sql Specifies a SQL statement to invoke which will populate the variables in the result.

Only required if there is no value attribute in the set tag. A call tag can also be specified here instead.

call Specifies a stored procedure call to invoke which will populate the variables in the result.

Only required if there is no value attribute in the set tag. A sql tag can also be specified instead.

result

Specifies a result tag with variables in the col tag body. The variables will be populated from the values returned by the SQL or stored procedure call.

Only required if there is either a sql or a call tag.

constructorArgs Specifies arguments to a constructor of a non-static method if that is being used to populate the variable to set.

Only required if we want to invoke a non-static method

methodArgs Specifies arguments to the method to be executed to populate the variable to be set

Required if we want to invoke a method to set the variable

Examples

Setting a variable ${var} to 14

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

Setting variable ${myquery.col1} from SQL

<set name="${myquery}"> <sql><stmt>select col1 from mytable where col2=45</stmt></sql>

Page 37: SQLUnit User Guide

<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

Page 38: SQLUnit User Guide

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

Page 39: SQLUnit User Guide

Attributes

None

Nested Elements

Table 11. Nested Elements for jndi

Name Description Required

arg A tag that specifies the name and value of a single JNDI property.

One or more arg elements are required.

Examples

A typical JNDI tag for getting a DataSource from JBoss

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

The methodinvoker Tag

Description

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

Page 40: SQLUnit User Guide

Syntax

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

Parent Elements

test

Attributes

Table 12. Attributes for methodinvoker

Name Description Required

static If set to true, specifies that the method being invoked is static. Valid values are true and false.

No, default is false.

class Specifies the full class name of the class in which the method is defined. Yes

method Specifies the method name to invoke. Yes

Nested Elements

Table 13. Nested Elements for methodinvoker

Name Description Required

constructorArgs Wraps a set of arg elements which specify the arguments, if any, that the constructor will need.

Not required for static methods, or if the class has a default no-args constructor.

methodArgs Wraps a set of arg elements which specify the arguments, if any, that the method will need.

Not required if the method takes no arguments.

Examples

A simple methodinvoker tag without method arguments

<methodinvoker class="net.sourceforge.sqlunit.utils.TestArgument"

Page 41: SQLUnit User Guide

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

sql Specifies the SQL statements to be executed, with or without replaceable parameters, that should executed on teardown. No

Page 42: SQLUnit User Guide

Name Description Required

foreach Contains bulk SQL statements such as SQL DELETE statements, which will be executed from within a foreach tag. No

include Specifies an external file which contains the SQL statements to be executed on teardown. No

Examples

Dropping some test tables on teardown

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

The foreach Tag

Description

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

Syntax

foreach ::= ((sql)*)

Parent Elements

setup prepare teardown

Attributes

Page 43: SQLUnit User Guide

Table 15. Attributes for foreach

Name Description Required

param

The name of the parameter that will be substituted in the SQL statement. For example, if the param is id, then all occurences of ${id} will be substituted by the current value of id.

Yes

values

A comma-separated list of values for the id. A value set can be specified within each element in the list. The elements of the set are separated with semi-colons. They are referred to later in the scope of the foreach tag as value_of_param.index. The index is 0-based.

No, either this or (start,stop,(step)) or (start,count,(step)) may be specified

start Specifies the starting value of the sequence of values for the param

No, values can be specified instead. If neither is specified, then start defaults to 0.

stop The ending value of the sequence of values for param No, count can be specified instead.

step The increment if a sequence is specified with start and stop No, defaults to 1.

count The number of entries in the sequence specified by start and stop.

No, stop can be specified instead.

Nested Elements

Table 16. Nested Elements for foreach

Name Description Required

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

Yes

Examples

A foreach used to prepare test data

<prepare> <sql><stmt>delete from foreachtest where 1=1</stmt></sql>

Page 44: SQLUnit User Guide

<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

connection-id

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

No, default Connection is used if not supplied

Page 45: SQLUnit User Guide

Name Description Required

id attribute defined.

extern

If specified, the value of this attribute points to a Java properties file which contains the information necessary to build the Connection. The file can be specified without path information if it can be found in the caller's CLASSPATH or specified as a relative path, as well as an absolute path on the file system. A sample properties file can be found in /cvs/test/postgresql/sqlunit.properties.

No

transaction-support

Indicates whether transaction support is provided by SQLUnit. Setting this to on (the default) will make SQLUnit treat each SQL or CALL as a single unit of work. Turning this to off will make SQLUnit not do any COMMITs or ROLLBACKs, it is left to the client to enforce boundaries of the unit of work by putting COMMIT and ROLLBACK in the SQL or stored procedures. Finally, turning this to implicit will put SQLUnit in auto commit mode, but SQLUnit will not do any explicit COMMIT or ROLLBACK. Transaction boundaries will either by enforced by the database if it supports it, or by the JDBC autocommit mechanism if not.

No, defaults to on

reconnect-on-failure

Will destroy the current Connection and rebuild it in the event of a test failure or exception. This is needed to work around buggy database drivers which leave the Connection in an inconsistent state in such cases.

No, defaults to false

server-name

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

No, defaults to the database server name

Nested Elements

Table 18. Nested Elements for connection

Name Description Required

driver Specifies the full Java class name for the JDBC driver. This is needed for building the Connection using JDBC.

Yes (JDBC)

url Specifies the JDBC URL to the database Yes (JDBC)

user Specifies the database user who will connect to the database Yes

Page 46: SQLUnit User Guide

Name Description Required

(JDBC)

password Specifies the password for the database user Yes (JDBC)

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

No (JDBC)

datasource

Specifies the name of a DataSource. By convention, this would look something like jdbc:/myDSN, where the DataSource object is actually stored under java:/comp/env/jdbc. This is likely to vary between JNDI servers and sites, so check with your administrator for the DataSource name to use

Yes (JNDI)

jndi

Empty tag, contains zero or more arg elements containing the name-value pairs to pass in when creating the Initial Naming Context that will be used to look up the DataSource object. These name-value pairs locate the JNDI service. Each arg element will correspond to a line in the jndi.properties file for the given JNDI server.

Yes (JNDI)

Examples

Specifying a JDBC connection for PostgreSQL

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

Specifying a JNDI connection with JBoss

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

Page 47: SQLUnit User Guide

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 Required

name

The name of the func instance. The value returned from the execution of the func element will be available in the SymbolTable as ${func.name_of_func} for later tests in the same suite.

Yes

lookup The name of the funcdef element to look up. Yes

connection-id

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

param0-9

The value for ${0}-${9} in the SQL string, if it exists. Note that if the variable is a string, you must supply the enclosing single quotes to indicate that. This type of query has no indication of the type of parameter being passed to it.

No

id The name or number of the sql call No

Nested Elements

Page 48: SQLUnit User Guide

Table 20. Nested Elements for func

Name Description Required

skip Indicates whether the func should be skipped or not. No

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

No

Examples

Calling a definition without parameters

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

Calling a custom function

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

The echo Tag

Description

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

Syntax

echo ::= EMPTY

Parent Elements

sqlunit

Page 49: SQLUnit User Guide

Attributes

Table 21. Attributes for echo

Name Description Required

name A name for this echo tag Yes, required by the SQLUnit logger

text The text to echo Yes

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

no

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

Page 50: SQLUnit User Guide

Attributes

Table 22. Attributes for include

Name Description Required

file Specifies a relative or absolute path name to the file containing the SQL statements. Yes

connection-id

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

No

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.

Page 51: SQLUnit User Guide

Syntax

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

Parent Elements

test

Attributes

Table 23. Attributes for result

Name Description Required

id The id of the result No

echo

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

No, defaults to false

Nested Elements

Table 24. Nested Elements for result

Name Description Required

outparam

Zero or more outparam elements may be specified, each representing an output parameter from the stored procedure. An outparam element can contain a resultset element in case of Oracle CURSOR types, or text information in case of non-CURSOR types.

No

updatecount Specifies the updatecount that is returned from the SQL or stored procedure call. No.

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

No.

Page 52: SQLUnit User Guide

Name Description Required

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

exception The exception tag is used to test for expected failures of a stored procedure in response to specific inputs.

No. Exception tags can coexist with outparam tags.

Examples

A multi-resultset result tag

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

A result containing an exception

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

Page 53: SQLUnit User Guide

</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 Description Required

arg A tag that specifies the name, type and value of the method argument.

One or more arg elements are required.

Examples

A methodArgs tag with different argument types

Page 54: SQLUnit User Guide

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

id The row id Yes

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

No, defaults to false

Nested Elements

Table 27. Nested Elements for row

Page 55: SQLUnit User Guide

Name Description Required

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

Examples

A simple row tag

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

The param Tag

Description

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

Syntax

param ::= (BODY)

Parent Elements

call sql paramset subdef

Attributes

Table 28. Attributes for param

Name Description Required

id Specifies the sequence number of the parameter to be replaced. Yes

Page 56: SQLUnit User Guide

Name Description Required

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

name Specifies the name of the parameter to be replaced. This has been added for readability to help stored procedure authors/testers to spot errors quickly.

No

type

Specifies the type of the parameter to be replaced. The type is a String, and has a one to one mapping to the datatypes defined in java.sql.Types. It also allows some additional Oracle specific types specified in net.sourceforge.sqlunit.OracleExtensionTypes. The value of the type is the same as the field name of the member variables in Types or OracleExtensionTypes classes. Thus if the type is Integer, the corresponding SQL type is java.sql.Type.INTEGER, and the string value that should be used here is INTEGER.

Yes

scale

Specifies the scale of the parameter value if the value maps to a BigDecimalType. This is an optional parameter. SQLUnit will try its best to guess the scale based on the actual value, but this is useful if the value cannot be specified or is NULL.

No

typename Specifies the name of a user-defined type. This is an optional parameter. No

is-null If set to true, indicates that this parameter is an SQL NULL value. Valid values are true or false.

No, default is false.

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

No, default is in.

Nested Elements

None

Examples

A simple param tag definition

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

Page 57: SQLUnit User Guide

The funcdef Tag

Description

Syntax

funcdef ::=

Parent Elements

setup

Attributes

Table 29. Attributes for funcdef

Name Description Required

name The name of the function definition. This will be used to call the definition from the func tag. Yes

query The SQL query with numbered replaceable parameters (see example below) which will be called to return the result. Yes

description User Documentation for the function definition. No

Nested Elements

None

Examples

Defining call to MySQL built-in function

Page 58: SQLUnit User Guide

<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

connection-id

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

No

Page 59: SQLUnit User Guide

Nested Elements

Table 31. Nested Elements for batchsql

Name Description Required

stmt Contains the SQL statement or stored procedure call in the body of the element

One or more stmt elements need to be specified

Examples

A typical batchsql call

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

The prepare Tag

Description

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

Syntax

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

Parent Elements

Page 60: SQLUnit User Guide

test

Attributes

None

Nested Elements

Table 32. Nested Elements for prepare

Name Description Required

set This tag sets values for SQLUnit test variables into the SQLUnit symbol table. No.

sql Identifies one or more SQL statements that should be executed as part of the per-test prepare No.

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

Examples

A foreach tag inside a prepare tag

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

The severity Tag

Description

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

Page 61: SQLUnit User Guide

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

Page 62: SQLUnit User Guide

Attributes

Table 33. Attributes for paramset

Name Description Required

id Specifies the id of the paramset element. Yes

Nested Elements

Table 34. Nested Elements for paramset

Name Description Required

param Specifies argument names and values to be passed into a batchcall call.

One or more

Examples

A paramset passed into an INSERT batchcall

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

The subdef Tag

Description

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

Page 63: SQLUnit User Guide

Syntax

subdef ::= ((param)*)

Parent Elements

setup

Attributes

Table 35. Attributes for subdef

Name Description Required

name The name of the subroutine definition. This will be used by the sub tag to specify the definition to look up. Yes

query The SQL or stored procedure call with replaceable parameters specified as in the sql or call tags. Yes

description A user-friendly description of the subroutine definition. Used for documentation No

type Specifies whether to treat the SQL as a stored procedure or SQL call when called. Valid values are sql and call.

No, defaults to sql

Nested Elements

Table 36. Nested Elements for subdef

Name Description Required

param

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

Zero or more parameters can be specified

Examples

A sample subroutine definition

Page 64: SQLUnit User Guide

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

id The column id Yes

name The column name No

type The XML name of the data type for the column Yes

Nested Elements

Page 65: SQLUnit User Guide

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

name The name of the batch test. THis is used by Yes

Page 66: SQLUnit User Guide

Name Description Required

SQLUnit to print progress messages to its log output

assert Specifies a single or comma-separated assertion that must hold true for the batch test.

No, defaults to equal if not specified.

failure-message

If specified, SQLUnit will print the user-supplied failure message if the test failed

No, default is no failure message

java-object-support

If set to true, Java Object Support is enabled for the test No, default is false

Nested Elements

Table 39. Nested Elements for batchtest

Name Description Required

skip Indicates whether the func should be skipped or not. No

classifiers

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

No

batchcall Specifies the set of batch calls to be made. Either batchcall or batchsql must be specified

No, but see Description

batchsql Specifies the set of batch calls to be made. Either batchcall or batchsql must be specified

No, but see Description

batchresult Specifies the expected return values from the call Yes

Examples

A typical batchtest specification

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

Page 67: SQLUnit User Guide

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

arg Specifies an individual argument One or more

Page 68: SQLUnit User Guide

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

Page 69: SQLUnit User Guide

Name Description Required

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

Nested Elements

Table 42. Nested Elements for batchcall

Name Description Required

stmt Contains the SQL statement or stored procedure to call in the body. Yes

param Specifies a set of param elements At least one needs to be provided

Examples

A typical batchcall tag

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

The outparam Tag

Page 70: SQLUnit User Guide

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

id The param-id of the outparam parameter. Yes

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

type Specifies the type name of the parameter. Yes

Nested Elements

Table 44. Nested Elements for outparam

Name Description Required

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

Not if the type is not CURSOR

Examples

Page 71: SQLUnit User Guide

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

Page 72: SQLUnit User Guide

Name Description Required

name Specifies a human-readable name for the test, which will be printed to the log as part of SQLUnit's progress messages.

Yes

assert Specifies a single or comma-separated list of assertions that must be true for the test. No, defaults to equal

failure-message

Allows the caller to supply an error message which should be displayed if the test failed.

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

java-object-support

If set to on, indicates that Java Object Support is turned on. Valid values are on and off.

No, default is off

expected-duration-millis

If specified, specifies that SQLUnit should time the test and fail the test if the test does not complete in the window specified +/- a percentage tolerance if specified, or 10% if not.

No

percentage-tolerance

If specified with expected-duration-millis, this specifies the percentage tolerance that SQLUnit will put on the expected-duration in order to calculate if the test should fail.

No, will default to 10 if expected-duration-millis is specified and percentage-tolerance is not specified.

Nested Elements

Table 46. Nested Elements for test

Name Description Required

skip Indicates whether the test should be skipped or not. No

classifiers

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

No

match Specifies zero or match elements that should be applied to match the result returned with that specified.

No

prepare Specifies SQL setup code that must be run on a per-test basis. No

Page 73: SQLUnit User Guide

Name Description Required

sql Specifies the SQL statement that must be run for this test.

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

call Specifies a stored procedure that must be run for the test.

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

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

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

dynamicsql Specifies a method which returns a String of dynamic SQL code that should be executed for this test.

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

sub Specifies a predefined and partially specified named SQL or stored procedure call

Either one of sql, call, methodinvoker, dynamicsql or sub func is required.

result Specifies the expected result from the test. Yes

Examples

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

The resultset Tag

Description

Page 74: SQLUnit User Guide

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

Syntax

resultset ::= (row)*

Parent Elements

result

Attributes

Table 47. Attributes for resultset

Name Description Required

id The resultset id Yes

partial If set to true, indicates that rows are partially specified. Valid values are true and false. Rows will be matched based on the row's id attribute.

No, defaults to false

rowcount Specifies the number of rows in the resultset. This is specified when the test client wants SQLUnit to only match the number of rows in the resultset, rather than the contents themselves.

No

order-by

A comma-separated list of column ids on which the rows in the resultset should be sorted. By default, both the resultset returned from the SQL or stored procedure call and the resultset that is specified will be sorted before comparing. The default sorting is by all the specified columns in the order in which they appear. The order-by attribute should be used only if the default sort needs to be overriden, for example, when the SQL or stored procedure returns ordered data. The sort order specified is ASCENDING, unless the column id is prefixed with a negative sign, which will cause it to be sorted in DESCENDING order. Setting order-by to NONE will turn off auto-sorting.

No

Nested Elements

Table 48. Nested Elements for resultset

Page 75: SQLUnit User Guide

Name Description Required

row A resultset can have zero or more row elements No

Examples

A simple resultset tag containing a single row

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

The match Tag

Description

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

Syntax

match ::= ((arg)*)

Parent Elements

diff

Attributes

Table 49. Attributes for match

Name Description Required

Page 76: SQLUnit User Guide

Name Description Required

resultset-id

If specified, defines the resultset(s) on which the user- defined matching criteria should be applied. Wildcards may be applied to specify multiple values using a '*', eg. '3*' or '*'. Ranges may be specified using an expression such as '1-2', or enumerations using an expression of the form '1,2,3'.

No, defaults to all resultsets if not specified

row-id If specified, defines the row(s) on which the user-defined matching strategy will be applied. Same wildcarding rules as defined in resultset-id apply.

No, defaults to all if not specified

col-id If specified, defines the column(s) on which the user-defined matching strategy should be applied. Same wildcarding rules as defined in resultset-id apply.

No, defaults to all if not specified

matcher

The full class name of the Matcher class that will be used for the user-defined matching. The Matcher class must implement the IMatcher interface and be available in the caller's CLASSPATH

Yes

Nested Elements

Table 50. Nested Elements for match

Name Description Required

arg Specifies the name and value of the parameters that are needed by the Matcher class, in addition to the actual column values, to compute the match.

Zero or more arg elements may need to be specified

Examples

Specifying a perccentage matcher with 20% tolerance

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

The sub Tag

Page 77: SQLUnit User Guide

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

lookup Specifies the name of the subdef element to look up. Yes

connection-id

The connection-id to use or use the default connection if not specified. No

Nested Elements

Table 52. Nested Elements for sub

Name Description Required

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

Zero or more

Examples

Calling a sub with parameters

Page 78: SQLUnit User Guide

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

code The error code for the expected exception No

message The error message for the expected exception No

Examples

Page 79: SQLUnit User Guide

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

Page 80: SQLUnit User Guide

Name Description Required

name The name of the test used by SQLUnit to report progress messages in its log output. Yes

assert Specifies a single assertion or a comma-separated list of assertions which must be true for this diff.

No, defaults to equal if not specified

failure-message

If specified, SQLUnit will print the user-supplied failure message if the test failed. No

java-object-support

If specified, Java Object Support is enabled for the test No, default is false

multi-threaded If set to true, runs the calls in parallel to save time No, default is false

(serial execution)

Nested Elements

Table 55. Nested Elements for diff

Name Description Required

skip Indicates whether the diff should be skipped or not. No

classifiers

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

No

match Provides overrides for the matching strategy on a per column basis No

prepare Allows per test setup calls No. Zero or one prepares can be specified.

sql Specifies the SQL statement to execute.

Either one of sql or call needs to be specified. Two and only two needs to be specified

call Specifies the stored procedure to execute.

Either one of sql or call needs to be specified. Two and only two needs to be specified.

Examples

Page 81: SQLUnit User Guide

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

Page 82: SQLUnit User Guide

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

name The name of the argument to be used Yes

value The value of the argument Yes

type The type of the argument No, but required in methodArgs and constructorArgs

Page 83: SQLUnit User Guide

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

Page 84: SQLUnit User Guide

foreach setup test diff teardown

Attributes

Table 57. Attributes for sql

Name Description Required

id The name or number of the sql call No

connection-id

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

No

Nested Elements

Table 58. Nested Elements for sql

Name Description Required

stmt Specifies the actual SQL statement, with or without replaceable parameters in the body of the tag.

Yes

param Occurs once for each replaceable parameter, if specified, for the SQL string.

Yes, if there are replaceable parameters in the SQL string.

Examples

Example of a simple sql tag

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

The dynamicsql Tag

Description

Page 85: SQLUnit User Guide

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

static If set to true, specifies that the method is a static method No, default is false (non-static method)

class Specifies the full class name where the method is defined Yes

method Specifies the method name to invoke Yes

id The name or number of the sql call No

connection-id

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

No

Nested Elements

Table 60. Nested Elements for dynamicsql

Name Description Required

Page 86: SQLUnit User Guide

Name Description Required

constructorArgs Wraps a set of arg elements which specify the arguments the constructor for the class will take

Not required if this is a static method, or if the class has a default null constructor

methodArgs Wraps a set of arg elements which specify the arguments to pass to the method

Not required if the method does not take arguments

Examples

A dynamicsql tag that invokes a static method

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

Extending SQLUnit: Writing your own tag handler

Overview

This is getting asked a lot nowadays, so I thought I will write this up for the benefit of those who are interested in doing this. To include a new tag, you need to do three things:

1. Write a Handler for your tag that implements the IHandler interface. 2. Add the mapping of the tag name to the actual handler class in the

etc/handlers.properties file 3. Update the list of "swappable" tags in handlers.properties, if applicable. 4. Modify the sqlunit.dtd file so that your new tag becomes part of the sqlunit

dialect.

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.

Page 87: SQLUnit User Guide

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

net.sourceforge.sqlunit src/net/sourceforge/sqlunit

The main package which contains the classes which did not fit neatly into the sub-packages detailed below. This used to be the only package for SQLUnit.

net.sourceforge.sqlunit.ant src/net/sourceforge/sqlunit/ant Contains the Ant SQLUnit task

net.sourceforge.sqlunit.beans src/net/sourceforge/sqlunit/beans

Contains beans to support the handlers. Older versions of SQLUnit handlers would return an Object from the process() method. They still return Objects, but now some of the handlers return well-defined Objects which can be cast to one of the beans in this package.

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

Page 88: SQLUnit User Guide

Package Name Location Description

All the implementations implement the IHandler interface.

net.sourceforge.sqlunit.types src/net/sourceforge/sqlunit/types

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

net.sourceforge.sqlunit.matchers src/net/sourceforge/sqlunit/matchers

Contains some user-defined matcher classes for use with the diff tag. All matchers implement the IMatcher interface.

net.sourceforge.sqlunit.reporters src/net/sourceforge/sqlunit/reporters

Contains reporter classes that can be used with SQLUnit. All reporters implement the IReporter interface.

net.sourceforge.sqlunit.utils src/net/sourceforge/sqlunit/utils

Contains utility classes whose methods get called by SQLUnit.

net.sourceforge.sqlunit.tools src/net/sourceforge/sqlunit/tools

Contains tools to generate test cases from existing SQL statements using console input and GUI based input.

Page 89: SQLUnit User Guide

Package Name Location Description

net.sourceforge.sqlunit.test test/java Contains some Java code used for LOB testing.

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

Contains code for the mock database framework for testing SQLUnit code independent of a database.

Test files for various databases test/*

Contains subdirectories named after various databases and contains stored procedures and test XML files for these databases.

SQLUnit registry files etc/*.properties

Most of SQLUnit is interface driven, and uses Factory classes to instantiate a named implementation of an interface. The registry files contain the mappings to the various implementations.

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.

Page 90: SQLUnit User Guide

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.

Page 91: SQLUnit User Guide

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

Page 92: SQLUnit User Guide

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:

Page 93: SQLUnit User Guide

<connection connection-id="4"> <driver>net.sourceforge.sqlunit.test.mock.SQLUnitMockDriver</driver> <url>jdbc:mock:net.sourceforge.sqlunit.test.mock.SQLUnitMockDatabase</url> <user /> <password /> <jarfile-url>file:build/,file:lib/mockrunner.jar,file:lib/log4j-1.2.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-jdbc-connection.properties" />

Page 94: SQLUnit User Guide

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

Page 95: SQLUnit User Guide

Transaction-Support

AutoCommit Set?

COMMIT on success?

ROLLBACK on failure?

on (default) No Yes Yes

off No No No

implicit 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 transaction-support=off.

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

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

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

Page 96: SQLUnit User Guide

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>

Page 97: SQLUnit User Guide

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

Page 98: SQLUnit User Guide

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

Page 99: SQLUnit User Guide

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

Page 100: SQLUnit User Guide

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.

Page 101: SQLUnit User Guide

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.

Page 102: SQLUnit User Guide

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

Page 103: SQLUnit User Guide

Error Message Corrective Action

Test File {filename} not found. {usage} The test file specified could not be found by SQLUnit. Check to see if the test file exists as specified on your operating system

Input Test File specified twice, specify either file or fileset

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

Cannot read Test File {filename}

SQLUnit does not have sufficient permissions to read the specified test file. Fix the permissions on the file at the operating system level

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

SQLUnit could not build the Connection object with the properties supplied. Verify that the properties for building the Connection are valid.

{resource} not found in specified Context

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.

Included file {filename} not found

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.

System Error: Element {element} is null

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.

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

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.

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

This error message may be encountered when using a User-defined matcher class in conjunction with the diff tag. A Matcher class always throws this type of exception if it did not get the inputs it expected or if it encounters an unexpected exception at runtime. The exception_message provides

Page 104: SQLUnit User Guide

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.

Cannot parse match pattern: {pattern}

The values for the resultset-id, row-id and col-id attributes to the Match tag can either be specified as exact numbers, a comma-separated enumeration or a range. It can also be omitted altogether or be specified as a *, both of which imply not to match against the particular filter. The error message means that the pattern was incorrectly specified or could not be parsed for some reason. The pattern will need to be modified to conform to the rules described above.

Matcher class {className} not available in CLASSPATH

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.

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

This is a variation of the standard message reported in case of test failures. This message is reported only by the Diff tag since that is the only tag which allows user-defined matching. In addition to the information reported by the test failure, it also reports the class name of the Matcher class currently being used. Corrective action is to fix the test.

Partial Match impossible at {position}: {reason}

Partial match at the specified position could not be done because of the reason specified.

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

The String value supplied in the param element could not be converted to the appropriate class dictated by the datatype mapping for that variable. You will most likely need to provide an override mapping specific to your database, or, if there is no suitable mapping, you/we could write a mapping class to handle this type.

Page 105: SQLUnit User Guide

Error Message Corrective Action

At {position}, could not convert {java_class_name} returned from database to {SQL_Type_Name}({SQL_Type})

The Object returned from the database is not of the same class that the class mapped to the SQL Type SQL_Type wraps. You will most likely need to provide an override mapping for your database, or, if there is no suitable mapping, you/we could write a mapping class to handle this type.

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

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.

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

SQLUnit could not find a mapping type for this specified type name. This is a non-standard (not in java.sql.Types) datatype, for which an override needs to be set in the mapping file for your database in usertypes.properties. If none of the existing mapping classes seem suitable, then you/we may have to write a new mapping class and map this to the datatype.

Parsing of {text} failed because {reason}

SQLUnit could not find the variable defined in the symbol table. If you do not mind missing variables, then you should set partialOk to true.

The symbol could not be found in the symbol table

The named symbol could not be found. Check your logic and verify that you have set the symbol before trying to use it.

Parameter {parameter_name} is not defined

The named parameter appears in the subdef as unspecified, but was not specified in the corresponding sub.

Statement {statement} ({number}) in include file {filename} returned result {error_code}

A SQL Statement in the include file referenced by the file name failed with the given error code. The SQL statements are indexed starting at 1.

Variable {variable_name} is invalid, should be in the format \${variable_name}

SQLUnit only accepts variables in the format ${variable_name}. Fix the name in

Page 106: SQLUnit User Guide

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.

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

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.

Could not convert to digest because {reason}

The reason will give more information as to why the digestion process failed. Large Objects such as BLOBs and CLOBs are digested and then compared with specified MD5 Checksums of files or the MD5 Checksum itself. If Java Object Support is enabled, and the large object represents a Serializable Java Object, or the datatype of the LOB is JAVA_OBJECT, then the object's toString() method will be called and the results returned.

Cannot invoke method {className}.{methodName}

This will usually be encountered when using the methodinvoker or dynamicsql tags, which depend on the results of invoking a specified method in a specified class in the CLASSPATH. The error will identify where the problem is happening, and will also include the error stack trace within the exception. To get the stack trace, rerun the test with debug set to true, and fix the problem that is causing this error to appear.

Assertion {assert} is not supported

The assertion provided in the assert attribute for this test element is not supported. Please check the documentation for a list of supported assertions.

Assertion {assert} failed ({reason}), expected {expected_result}, but got {actual_result}, {failure_message}

The specified assertion failed.

Expected to complete in {expected-duration} ms +/- {percent-tolerance}%, but took {actual_duration}ms

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

Page 107: SQLUnit User Guide

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: {total_tests_run}, failures: {number_of_failed_tests}, errors: {number_of_errors_encountered}

This message is reported at the end of each SQLUnit test file if there was an error or failure running the tests in the file.

One or more SQLUnit Tests failed, see the {console|logfile} for details

This message is reported at the end of an SQLUnit run, spanning one or more test files, if there was a failure or an error in any one of the tests. The output will describe the exact nature of the failure(s).

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

The entire error message is returned, usually from the JVM. The exception class specifies the Exception class name. Log a bug against the SQLUnit project if the meaning is not clear enough, otherwise take the necessary action to fix the condition that is causing it. Turning on the debug attribute for the sqlunit ant task will also show the stack trace for reporting or for analysis.

Supported Datatypes

List of currently supported datatypes

Page 108: SQLUnit User Guide

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

Table 1. SQLUnit Supported Datatypes

Type Class Type Name Server

Allows

Input

Allows

Output

Is Sortab

le Wrapper for

TextType NTEXT

Microsoft SQL Server

No Yes No java.io.InputStream

ClobType CLOB Any No Yes No java.sql.Clob

ClobType LONGVARCHAR

MySQL No Yes No java.sql.Clob

ArrayType ARRAY Any no yes no java.sql.Array

JavaObjectType JAVA_OBJECT Any No Yes No java.lang.Object

UnsupportedType DATALINK Any No No No n/a

UnsupportedType DISTINCT Any No No No n/a

UnsupportedType NULL Any No No No n/a

UnsupportedType REF Any No No No n/a

UnsupportedType STRUCT Any No No No n/a

IntegerType INTEGER Any Yes Yes Yes java.lang.Integer

IntegerType SMALLINT Sybase ASA Yes Yes Yes java.lang.Integer

BinaryType BINARY Any No Yes No java.io.InputStream

Page 109: SQLUnit User Guide

Type Class Type Name Server

Allows

Input

Allows

Output

Is Sortab

le Wrapper for

BinaryType LONGVARBINARY Any No Yes No java.io.InputStrea

m

ByteType TINYINT Any Yes Yes Yes java.lang.Byte

TimeType TIME Any Yes Yes Yes java.sql.Time

OracleCursorType CURSOR Oracle No Yes No java.sql.ResultSet

DateType DATE Any Yes Yes Yes java.sql.Date

LongType BIGINT Any Yes Yes Yes java.lang.Long

ByteArrayType BINARY Any No Yes No byte[]

ByteArrayType VARBINARY Any No Yes No byte[]

BigDecimalType DECIMAL Any Yes Yes Yes java.math.BigDec

imal

BigDecimalType NUMERIC Any Yes Yes Yes java.math.BigDec

imal

BlobType BLOB Any no yes no java.sql.Blob

ShortType SMALLINT Yes Yes Yes java.lang.Short

OtherType OTHER Any No Yes No java.lang.Object

DoubleType DOUBLE Any Yes Yes Yes java.lang.Double

TimestampType TIMESTAMP Any Yes Yes Yes java.sql.Timestam

p

BooleanType BIT Any Yes Yes Yes java.lang.Boolean

BooleanType BOOLEAN Any Yes Yes Yes java.lang.Boolean

StringType CHAR Any Yes Yes Yes java.lang.String

StringType LONGVARCHAR Any Yes Yes Yes java.lang.String

StringType VARCHAR Any Yes Yes Yes java.lang.String

StringType NCHAR

Microsoft SQL Server

Yes Yes Yes java.lang.String

Page 110: SQLUnit User Guide

Type Class Type Name Server

Allows

Input

Allows

Output

Is Sortab

le Wrapper for

StringType NVARCHAR

Microsoft SQL Server

Yes Yes Yes java.lang.String

StringType FIXED_CHAR Oracle Yes Yes Yes java.lang.String

FloatType REAL Any Yes Yes Yes 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 Old Mapping New Mapping

FLOAT FloatType (java.lang.Float)

DoubleType (java.lang.Double)

NUMERIC DoubleType (java.lang.Double)

BigDecimalType (java.math.BigDecimal)

VARBINARY

BinaryType (java.io.InputStream)

ByteArrayType (byte[])

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

Extending SQLUnit: Adding support for your own datatypes Differences in implementation among different JDBC drivers and databases will require you to add support for your database if your datatype is not listed above, or if it uses a

Page 111: SQLUnit User Guide

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

Page 112: SQLUnit User Guide

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:

Page 113: SQLUnit User Guide

Table 1. SQLUnit Assertions

Assert-String Description Used In

none Asserts nothing about the generated and the actual results. This is typically used to disable assertions for the particular test.

diff,test

equal

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.

diff,test

not-equal Asserts that the two results are not equal. This is an inversion of the equals assertion. diff,test

exception-matches Asserts that the two results have identical exceptions. diff,test

not-exception Asserts that the result returned is NOT an exception diff,test

number-outparams-equal

Asserts that the two results have the same number of outparam elements. diff,test

outparams-equal Asserts that the outparams in both the results are equal. diff,test

update-counts-equal Asserts that the update counts in the two results are equal. diff,test

number-of-resultsets-equal

Asserts that the number of resultsets in the two results are equal. diff,test

resultsets-equal Asserts that the resultsets are equal, or match if the test contains embedded match tags. diff,test

resultset-values-equal

Asserts that the columns in the specified and actual resultset have the same values. No assertion is made for types. diff,test

resultset-types-equal

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

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.

diff,test

equal

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.

batchtest

Page 114: SQLUnit User Guide

Assert-String Description Used In

not-equal Asserts that the two batchresults are not equal. This is the inverse of the equal assertion. batchtest

failed-at-equal 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.

batchtest

expected-count-equal

Asserts that the number of update counts specified in the expected batchresult, either as an expected-count attribute, or listed explicitly, is the same as the one in the generated batchresult.

batchtest

updatecount-equal

Asserts that the updatecount values are the same for the result specified by batchresult and the result generated by the call to batchsql or batchcall.

batchtest

fail-with-failure 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:

Page 115: SQLUnit User Guide

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

Page 116: SQLUnit User Guide

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

Page 117: SQLUnit User Guide

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

Page 118: SQLUnit User Guide

Pattern Description Example

*

Specifying a '*' for a given attribute means that SQLUnit will match all corresponding elements represented by this attribute in the result element. It is equivalent to not specifying the attribute at all.

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

Single number (n)

Specifying a single number for a given attribute means that SQLUnit will match all corresponding elements having this attribute in the result element.

resultset-id="*" row-id="*" col-id="3" will match the third column in all rows in all resultsets

Range of numbers (m-n)

Specifying a range of numbers for a given attribute means that SQLUnit will match all corresponding elements whose attribute falls in the specified range.

resultset-id="*" row-id="1" col-id="1-3" will match the first, second and third columns in the first row for all resultsets using this matcher.

Enumeration of numbers (m,n,p)

Specifying an enumeration for a given attribute means that SQLUnit will match all corresponding elements whose attributes match one of the numbers in the enumeration.

resultset-id="*" row-id="1" col-id="1,2,3" will do the same thing as the above example.

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

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

resultset-id="*" row-id="1" col-id="1-3,5,6" will match the first, second, third, fifth and sixth columns in the first row for all resultsets using the matcher.

List of currently supported Matchers Table 2. SQLUnit Matchers

Matcher Name Author Description Arguments

AllOrNothingMatcher

Sujit Pal ([email protected])

The AllOrNothingMatcher is an implementation of the IMatcher interface used to define rulesets for matching columns in SQLUnit. This

match : Always returns true if

Page 119: SQLUnit User Guide

Matcher Name Author Description Arguments

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.

set to true, else always returns false.

SignificantDigitsMatcher

Tim Cull ([email protected])

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

signif-digits : Number of significant digits to match to

TypelessMatcher

Sujit Pal ([email protected])

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

None : -

RangeMatcher Sujit Pal ([email protected])

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

tolerance : An absolute tolerance value.

Page 120: SQLUnit User Guide

Matcher Name Author Description Arguments

PercentageRangeMatcher

Sujit Pal ([email protected])

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

pc-tolerance : A percentage tolerance value.

TypelessPercentageMatcher

Chris Watts ([email protected])

The TypelessPercentageMatcher matches columns which may differ by a specified percentage. This can be useful when comparing numeric values across data types which have different default precisions.

pc-tolerance : A percentage tolerance value.

ForgivingNullMatcher

Tim Cull ([email protected])

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

None : -

Page 121: SQLUnit User Guide

Matcher Name Author Description Arguments

ExpressionMatcher

Sujit Pal ([email protected])

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

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

Extending SQLUnit: Writing your own Matcher

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.

/*

Page 122: SQLUnit User Guide

* $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 02111-1307, USA. */ package net.sourceforge.sqlunit.matchers; (3) import java.util.Map; (4) import net.sourceforge.sqlunit.IErrorCodes; import net.sourceforge.sqlunit.IMatcher; import net.sourceforge.sqlunit.SQLUnitException; /** (5) * The PercentageRangeMatcher is an implementation of the IMatcher interface * used to define rulesets for matching columns in SQLUnit. This matcher * will accept a percentage tolerance value and check to see that the target * is within (+/-) tolerance percent of the source. * Arguments: * pc-tolerance : a percentage tolerance value. * @author Sujit Pal ([email protected]) * @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.

Page 123: SQLUnit User Guide

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

Page 124: SQLUnit User Guide

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)

Page 125: SQLUnit User Guide

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?

Page 126: SQLUnit User Guide

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

TextReporter Rob Nielsen ([email protected])

SQLUnit reporter that writes its text to standard output. This is the default reporter for SQLUnit.

CanooWebTestReporter Rob Nielsen ([email protected])

SQLUnit Reporter that works with the Canoo Web Test framework. Generates XML that can be converted to output for the Canoo Web Test console.

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

Setting up your Reporter for use with SQLUnit

Page 127: SQLUnit User Guide

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>

Page 128: SQLUnit User Guide

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

Page 129: SQLUnit User Guide

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

Page 130: SQLUnit User Guide

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

Page 131: SQLUnit User Guide

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.

Page 132: SQLUnit User Guide

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.

Page 133: SQLUnit User Guide

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>