Documente Academic
Documente Profesional
Documente Cultură
Sujit Pal
Copyright 2003-2004 The SQLUnit Team
Table of Contents Introduction Alternative Forms of this document Printer Friendly Format Downloadable Format Historical Trivia Porting Information License Information Installing Downloading the source Required JAR files Running the install script Running SQLUnit Command Line Interpreting the output SQLUnit GUI Tool SQLUnit TUI Tool SQLUnit XSL Transform Tool SQLUnit TestDocsGenerator Tool SQLUnit Canoo2HTML Tool SQLUnit Tags List of currently supported tags The sqlunit Tag The setup Tag The skip Tag The subparam Tag The batchresult Tag The call Tag The set Tag The classifiers Tag The jndi Tag The methodinvoker Tag The teardown Tag The foreach Tag The connection Tag The func Tag The echo Tag The include Tag
The result Tag The methodArgs Tag The row Tag The param Tag The funcdef Tag The batchsql Tag The prepare Tag The severity Tag The paramset Tag The subdef Tag The col Tag The batchtest Tag The constructorArgs Tag The batchcall Tag The outparam Tag The test Tag The resultset Tag The match Tag The sub Tag The exception Tag The diff Tag The category Tag The arg Tag The sql Tag The dynamicsql Tag Extending SQLUnit: Writing your own tag handler Overview Finding your way around Writing your handler Adding the tag to handler class mapping Updating the list of valid swappable child tags Adding the DTD for the new tag Getting Connections Basic JDBC Connection JDBC Connection with properties Basic JNDI Connection JDBC Connection using local JAR file Connection Properties from external file Passing in a Connection object to SQLUnit Controlling Transactions Using variables Setting explicitly from scalar Setting explicitly from query Setting explicitly from Java method call Setting variables from ant Setting variables implicitly
Including Files Including XML Files SQLUnit Include Files Error Messages Supported Datatypes List of currently supported datatypes Extending SQLUnit: Adding support for your own datatypes Using an existing implementation for an unlisted datatype Overriding the implementation for a datatype Adding a new implementation for a new datatype SQLUnit Assertions List of currently supported Assertions Extending SQLUnit: Adding your own Assertion Large Object (LOB) Support Handling Object output from ORDBMS Handling BLOB and CLOB outputs Java Object Support User-Defined Matching What is User-Defined Matching Wildcarding rules for Match Attributes List of currently supported Matchers Extending SQLUnit: Writing your own Matcher Setting up your Matcher for use with SQLUnit User-Defined Reporting What is User-Defined Reporting? List of currently supported Reporters Extending SQLUnit: Writing your own Reporter Setting up your Reporter for use with SQLUnit User-Defined Test Grouping and Test Skipping What are User-Defined Test Groups Declaring the group of a test SeverityHandler CategoryHandler Extending SQLUnit: Write your own classification Skipping Tests with <skip> Using the Mock Testing Framework Overview Writing your own mock procedures Writing tests against the mock procedures
Introduction
SQLUnit is a regression and unit testing harness for testing database stored procedures. The test suite is written as an XML file. The SQLUnit harness itself is written in Java and
uses the JUnit unit testing framework to convert the XML test specifications to JDBC calls and compare the results generated from the calls with the specified results. Stored procedures are often used to provide an object abstraction to the underlying relational data store. The object abstractions are consumed by Java middleware programs. In shops where this is the case, Java programmers often use the popular JUnit testing framework to unit test their code. Using SQLUnit to unit test the database stored procedures thus becomes a natural extension of the unit testing process, and provides unit testing coverage for a layer that is often neglected in this respect. Since the stored procedure author need not be a Java coder, and may not even know Java or JUnit, SQLUnit provides an XML language to write test suites. Stored Procedure authors who know Java and/or can write JUnit tests will find that the repetitive nature of JDBC programming would, over time, encourage refactoring the common parts of the tests into utility classes. SQLUnit already does this, except it goes one step further and abstracts the test specification out of Java into XML. The XML itself is fairly small and easy to learn for anybody and should be instantly intuitive to a person who is already familiar with JUnit. Further, since SQLUnit uses JDBC to communicate with the database, it should be possible to write unit tests for stored procedures for any database that provides a JDBC driver. Some relatively painless porting may be needed for each new DBMS or driver, due to vendor-specific extensions to the SQL standard or restrictions of the JDBC API.
Downloadable Format
This is a tar.gz file which can be downloaded to your personal computer, so you dont have to connect to the network every time you want to read the SQLUnit documentation. You will have to make a subdirectory and gunzip and untar inside that subdirectory. Here is the sequence of instructions (for Unix/Linux users) to build the documentation locally.
$ $ $ $ mkdir sqlunit-docs mv /download/dir/htdoc.tar.gz sqlunit-docs cd sqlunit-docs tar xvzf htdoc.tar.gz
You can access this from the browser using a file URL from your browser, like so:
file:///path/to/sqlunit-docs/index.html
Make sure to check back from time to time, though, since there may have been bug fixes and feature enhancements in future releases that you could probably take advantage of. Download User Guide.
Historical Trivia
This project was inspired by a regression testing tool for Informix stored procedures, originally written by Mark H Sullivan. It was written using a combination of Java, shell scripts and make, and supported a very rudimentary set of directives for running the stored procedures in a certain sequence and comparing the outputs with the specified outputs. All error checking was at runtime, so we had to be careful to avoid "syntax" errors such as using double quotes instead of single quotes. It was also very hard to extend, as I found out when trying to add some new directives. I re-wrote this tool in C using lex and yacc for the command processor, and Informix ESQL/C to call the procedures themselves. This fixed some of our problems, because the problems associated with debugging missing quotes and newlines were caught earlier on. However, the tool would mysteriously segfault occasionally. Some leaks were caught with dbmalloc, but the problems still remained, so we had to abandon the tool as well. I re-wrote the tool again using Perl. It used the same input files as the original tool and the C tool, and provided early error checking similar to the C tool. It also provided more intuitive error messages when the tests failed. Some months later, on another project, I was introduced to JUnit based on a chance remark of a colleague, and software development has never been the same for me since. I was almost an instant JUnit convert.
Some years later, on yet another project, I learned how to associate Java handler classes with XML tags, which forms the basis of much of the architecture of SQLUnit. Over the years, and especially in my current job, I have been exposed to interface driven development and the use of factories to instantiate implementation classes, which also form the basis of the SQLUnit design. I started SQLUnit as a Sourceforge project when I was tasked with writing the persistence layer (including development of Sybase stored procedures) for a new application. While JUnit sufficed to test the Java portion of the code, I missed having a regression tool for stored procedures around. So based on what I thought would be useful, and on the lessons learned during the last few iterations of building a similar tool, I set about writing one in my spare time (on my train ride to and from work). I ultimately ended up delivering the project without using SQLUnit, but I hope to use it the next time I have to write a similar application. SQLUnit would be different from its predecessors in that it would be based on a standard testing framework (Junit), have a clean, expressive and extendable command syntax (XML), and be able to run on any database which provided a JDBC driver. SQLUnit in its first incarnation only provided for exact equality comparisons between specified results and the results of SQL or stored procedure calls. It used JDBC to get a database connection. Over time, SQLUnit has evolved, so we now have around 6 different ways of getting a database connection, including one by looking up a named DataSource from a JNDI server. The original code only had around 10 tags, now we have around 30. SQLUnit has been put to various uses by its user community, apart from the one for which it was intended, namely regression testing stored procedures. It has been used to regression test new JDBC drivers against old ones and against a known set of specifications. It has been used to validate ETL runs from operational databases to a data warehouse with a specified degree of accuracy. It has been bundled along with testing suites for commercial applications. SQLUnit has benefited tremendously from the feedback and criticism from the community, many of which were very insightful and knowledgeable, and have formed the basis of enhancements and features to SQLUnit. It has also benefited from the numerous patches, bug fixes and porting efforts by various members of the community. In short, SQLUnit is what it is today because of the strong community support that it has. Thank you, SQLUnit would not be what it is today without you.
Porting Information
Work is currently going on to port SQLUnit (or to just make it run on) various databases. Here is the status of the porting efforts till date. If you are running SQLUnit with databases other than those listed below, let us know, and we will add in your information as well. If you are planning to run SQLUnit with a database not listed below, you probably will have no problems. Most non-standard features (such as Oracle CURSORs, multiple resultsets in Sybase, etc) have been identified and accounted for in the SQLUnit code. In case you do have problems, however, please let us know on the forum. Table 1. Ports of SQLUnit to different databases Database Name (Version) PostgreSQL 7.3
Porter
Status Complete. PostgreSQL was used to do a proof of concept of the initial release of SQLUnit. It has been used thereafter to test new features as they are developed. MySQL does not support stored procedures or functions, but it has been used to test the batch tags, the diff and the sql tags. It plays nicely with SQLUnit. MySQL will support stored functions tentatively in version 5, so we will revisit this when that happens. Complete, with a lot of patches and bug reports from Sahib and Andrei. Thanks to Andrei for running SQLUnit through a very thorough testing with Oracle and getting the last few Oracle CURSOR specific bugs ironed out. Complete, with lots of testing support from Soumya Shetty and Lakshmi Started June 05, 2003. In progress. The stored procedures were written in C, the Java stored procedures had issues running under JDK 1.4. Still need to build a test suite and run them. In progress Not started. This should work, but has not been tested. The test cases are available in CVS, but the JDBC driver that I had available had issues with working with PAM authentication on Linux. If someone has a
Sujit Pal
MySQL 3.4
Sujit Pal
Oracle
Sahib Singh Wadhwa, Andrei Grigoriev, Sujit Pal Sowmya Shetty, Lakshmi, Sujit Pal
IBM DB2
Sujit Pal
Campbell BoucherBurnett
Porter
Status working setup of Informix, then I would appreciate you running the tests and letting me know the results. On May 29, 2005, James contributed his suite of MS SQL Server tests that he uses to regression test SQLUnit. Earlier, on May 20, 2004, Paul reported that he has been using SQLUnit to test MS SQL Server 2000 for some time, but has not tested the LOB feature yet. Complete, test suite included with distribution (test/asa/README)
David Fishburn
License Information
English is broken, it does not distinguish between free beer and free speech. Spanish on the other hand, distinguishes between gratis and libre. Free software is libre software. --Miguel de Icaza SQLUnit is libre software. It is also gratis software, but if someone wants to earn money by installing, customizing or training people about it for a fee, I dont see any problems with that. SQLUnit is distributed under the terms of the GNU General Public License, which gives you the right to copy, modify and re-distribute it either in source or binary form. Using SQLUnit does not obligate you to contribute back to the project any enhancements or fixes you make to it, but we appreciate the gesture if you do so. The full text of the GNU General Public License can be found here.
Installing
Downloading the source
The latest source code can be downloaded from sourceforge via anonymous cvs. The CVSROOT environment will need to be set as follows (for Linux using bash):
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.
How it is used
Licensing
URL
SQLUnit is written using Java 1.4_02, so you will need to install the JRE if you Java 1.4 are just going or later to use it, or the JDK if you are going to do development with it.
http://java.sun.com/
Yes
No
Ant is used to build the SQLUnit package. An Ant 1.6 Apache License ant task is or later provided to run SQLUnit from within Ant.
http://ant.apache.org/
Yes, although you can also just No call it from the comman d line with
How it is used
Licensing
URL
Yes
SQLUnit uses JDOM to do Apache License its XML parsing and rendering.
http://www.jdom.org/
Yes
Yes
Apache This is used Log4J for logging in Apache License 1.2.8 or SQLUnit. higher Apache Used by Xerces- Canoo J 1.4.4 WebTest
Yes
Yes
SQLUnit uses this to set up and run a set of Mock tests MockR against a Apache style unner Mock license 0.2.7 database that consists of method calls which are introspected. Apache Used by Commo MockRunner ns-Lang
No. This is only needed if you want to run http://mockrunner.sourcef the mock Yes orge.net/ test suite to make sure everythi ng worked. http://jakarta.apache.org/c No, see ommons/ above. Yes
Apache License
How it is used convert source code annotations into Docbook XML which is then included in this document.
Licensing
URL
SQLUnit uses Jalopy to provide a guide for coding conventions used in Apache SQLUnit. Jalopy BSD License Contributed 0.6.2 code typically passes through Jalopy to get reformatted before inclusion. SQLUnit uses CheckStyle to audit code style on an ongoing basis. The checks that have been Apache configured are Checkst LGPL a union of the yle 3.4 sun_checks.x ml and BlochEffectiv eJava.xml files supplied with the distribution.
http://jalopy.sourceforge. net/
How it is used Some checks have been relaxed or eliminated where it did not match the coding style for SQLUnit.
Licensing
URL
SQLUnit uses JavaCC to generate a parser for include files JavaCC based on the BSD License 3.2 grammar specified in the IncludeFilePa rser.jj. SQLUnit uses JEXL as the Apache Expression Commo parser and ns Apache License evaluator for JEXL its 1.0 ExpressionMa tcher matcher. Apache Commo ns Loggin g 1.0 JEXL needs Apache Logging as a runtime dependency.
https://javacc.dev.java.net Yes /
Yes
Yes
Apache License
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.
So to create a SQLUnit JAR file from scratch, run the following ant directive to create the sqlunit.jar file in your dist directory.
$ ant install Buildfile: build.xml setup: clean: [delete] Deleting directory /home/spal/sqlunit/build [mkdir] Created dir: /home/spal/sqlunit/build compile: [javac] Compiling 18 source files to /home/spal/sqlunit/build [copy] Copying 1 file to
/home/spal/sqlunit/build/net/sourceforge/sqlunit distclean: [delete] Deleting directory /home/spal/sqlunit/dist [mkdir] Created dir: /home/spal/sqlunit/dist install: [jar] Building jar: /home/spal/sqlunit/dist/sqlunit-1.0.jar [echo] File sqlunit-1.0.jar in dist BUILD SUCCESSFUL Total time: 3 seconds
Running
SQLUnit Command Line
SQLUnit is really a single JUnit test which reads the SQLUnit test XML file to determine what it should do. Starting with version 1.8, the SQLUnit Ant task comes with new configuration options. Since the sqlunit task is an optional task, the task must be first declared to Ant using a taskdef, like this.
<!-- This is specified once per buildfile --> <target name="def"> <taskdef name="sqlunit" classname="net.sourceforge.sqlunit.ant.SqlunitTask"> <classpath> <pathelement location="/your/location/for/sqlunit.jar" /> </classpath> </taskdef> </target>
In order to run a single test file called test/mysql/test.xml from within Ant using the sqlunit task, the XML snippet in the build file would look like this.
<!-- This is repeated for each test or group of tests in case of nested filesets --> <target name="run-postgres-test" depends="def"> <sqlunit testfile="test/postgres/test.xml" haltOnFailure="false" debug="false" /> </target>
Notice the new attributes haltOnFailure and debug. Both of them are optional attributes and SQLUnit will revert back to its default behavior if they are not specified. We will discuss them in more detail below. In cases where you would like to run a group of SQLUnit test files, you can use nested filesets instead of the testfile attribute. So if your build base directory was the same as your stored procedure repository root, say /opt/database, under which you have organized your stored procedures into different subdirectories based on function, something like this:
/opt/database |-- stored_procedures | | | |-- accounting | |-- human_resources | |-- infotech | |-- ...
Further, let us assume that you store your SQLUnit test XML files for each functional silo along with the stored procedures, so your accounting SQLUnit XML test file is /opt/database/accounting/accounting_tests.xml. To specify that you wish to run all the .xml under the stored procedures directory, you would specify the sqlunit target like this:
<target name="run-all-test" depends="compile,def"> <sqlunit haltOnFailure="false" debug="false"> <fileset dir="stored_procedures"> <include name="**/*.xml" /> </fileset> </sqlunit> </target>
You can specify multiple nested fileset elements if needed. Just remember that the testfile attribute specification and the nested fileset specifications are mutually exclusive. SQLUnit will give you an error if you attempt to specify both. The haltOnFailure and the debug attributes can be used to control the behavior of SQLUnit. This is described below. Table 1. Configuring SQLUnit behavior Attribute Name Valid Values Description
If set to "true", SQLUnit will stop executing the current "true" or test file if there is a test failure or exception. If multiple haltOnFailure "false", default files are specified using nested fileset elements, then it "false" will continue to the next file.
Attribute Name
Valid Values
Description
debug
If set to "true", haltOnFailure will also be set to "true" "true" or regardless of the value set. Setting debug to "true" will "false", default output a very verbose trace log on the console. The trace "false" contains a line for every method entered while the test was running. If not set or set to an empty string, the logging for SQLUnit will be sent to STDOUT. If set, it will be sent to the file specified. The filename can be absolute or relative to the directory where the ant command is being run. This feature has been available since 2.2 If not set or set to an empty string, the default reporter would be used. The default reporter will print a formatted report of the tests that were executed. This feature is available starting with version 3.6
logfile
logformat
If the logfile attribute is not set, SQLUnit will log its output to STDOUT and its exceptions, if any, to STDERR. This behavior has been introduced since version 2.2. Scripts running SQLUnit versions prior to this and depending on the older behavior (all logging to STDERR) will need to change the scripts to add logging to STDOUT as well). SQLUnit will report all its status messages, including error messages to STDOUT. If there were one or more failures at the end of the build, then it will send a BuildException to Ant which will report a BUILD FAILED message to the user. Starting with version 2.2, SQLUnit can be used with Ant's own BuildLoggers. The code has been tested with the org.apache.tools.ant.DefaultLogger and org.apache.tools.ant.XmlLogger loggers. To use a specific buildlogger, you can either use Ant's record tag or supply the log file name and logger class name in the ant command line, like so:
ant -logfile ${logfile_name} -logger ${logger_class_name} ${target}
Do not specify a logfile attribute in the sqlunit task when specifying the logfile and logger on the command line. This can lead to unexpected behavior. Alternatively, you can use either the depends attribute of target or the antcall task to run more than one suite of tests with a single call. However, using the nested fileset elements in the sqlunit task seems to be the more obvious way to do it. The options described for running SQLUnit from the command line using java or using the junit task under Ant in the documentation for versions prior to version 1.8 are deprecated and no longer supported. These approaches were cumbersome and not
widely used.
Buildfile: build.xml init: test-ant-task: [sqlunit] Getting connection... [sqlunit] Setting up test... [sqlunit] Running test[1]: Adding department HR [sqlunit] Running test[2]: Adding department InfoTech using nonCallable form [sqlunit] Running test[3]: Adding Employee John Doe to InfoTech [sqlunit] Running test[4]: Adding John Doe again [sqlunit] Running test[5]: Adding Jane Doe to HR [sqlunit] Running test[6]: Adding Dick Tracy to InfoTech [sqlunit] Running test[7]: Updating Hourly Rate for John [sqlunit] Running test[8]: Looking up John Doe by name [sqlunit] Running test[9]: Looking up all employees in InfoTech [sqlunit] Running test[10]: Adding timecard for John [sqlunit] Running test[11]: Adding another timecard for John [sqlunit] Running test[12]: Adding timecard for Dick [sqlunit] Running test[13]: Getting monthly report for InfoTech [sqlunit] Tearing down test... [sqlunit] . [sqlunit] Time: 3.795 [sqlunit] [sqlunit] OK (1 tests) [sqlunit] BUILD SUCCESSFUL Total time: 4 seconds
By default, SQLUnit will not consider a test failure as sufficient reason to stop the test suite from completing, since a test suite can contain unrelated tests that may not depend on each other. If you need SQLUnit to stop processing the suite after it encounters a test failure, set the haltOnFailure attribute to true. Here is an example of SQLUnit generating an error, with inline pointers to comments.
Buildfile: build.xml init: test-ant-task: [sqlunit] Getting connection... [sqlunit] Setting up test... [sqlunit] Running test[1]: Adding department HR [sqlunit] Running test[2]: Adding department InfoTech using nonCallable form [sqlunit] Running test[3]: Adding Employee John Doe to InfoTech [sqlunit] Running test[4]: Adding John Doe again [sqlunit] Running test[5]: Adding Jane Doe to HR [sqlunit] Running test[6]: Adding Dick Tracy to InfoTech [sqlunit] Running test[7]: Updating Hourly Rate for John [sqlunit] Running test[8]: Looking up John Doe by name [sqlunit] Running test[9]: Looking up all employees in InfoTech [sqlunit] Running test[10]: Adding timecard for John [sqlunit] Running test[11]: Adding another timecard for John [sqlunit] Running test[12]: Adding timecard for Dick [sqlunit] Running test[13]: Getting monthly report for InfoTech [sqlunit] No match on variable at [rset,row,col]=([1,2,4] (1) [sqlunit] *** expected: (2) [sqlunit] <result> [sqlunit] <resultset id="1"> [sqlunit] <row id="1"> [sqlunit] <col id="1" type="VARCHAR">Information Technology</col> [sqlunit] <col id="2" type="VARCHAR">Dick Tracy</col> [sqlunit] <col id="3" type="INTEGER">13</col> [sqlunit] <col id="4" type="NUMERIC">50.00</col> [sqlunit] <col id="5" type="NUMERIC">650.00</col> [sqlunit] </row> [sqlunit] <row id="2"> [sqlunit] <col id="1" type="VARCHAR">Information Technology</col> [sqlunit] <col id="2" type="VARCHAR">John Doe</col> [sqlunit] <col id="3" type="INTEGER">16</col> [sqlunit] <col id="4" type="NUMERIC">56.00</col (3) [sqlunit] <col id="5" type="NUMERIC">880.00</col> [sqlunit] </row> [sqlunit] </resultset> [sqlunit] </result> [sqlunit] *** but got: (4) [sqlunit] <result> [sqlunit] <resultset id="1"> [sqlunit] <row id="1"> [sqlunit] <col id="1" type="VARCHAR">Information Technology</col> [sqlunit] <col id="2" type="VARCHAR">Dick Tracy</col> [sqlunit] <col id="3" type="INTEGER">13</col> [sqlunit] <col id="4" type="NUMERIC">50.00</col> [sqlunit] <col id="5" type="NUMERIC">650.00</col> [sqlunit] </row> [sqlunit] <row id="2"> [sqlunit] <col id="1" type="VARCHAR">Information
Technology</col> [sqlunit] <col id="2" type="VARCHAR">John Doe</col> [sqlunit] <col id="3" type="INTEGER">16</col> [sqlunit] <col id="4" type="NUMERIC">55.00</col> (5) [sqlunit] <col id="5" type="NUMERIC">880.00</col> [sqlunit] </row> [sqlunit] </resultset> [sqlunit] </result> [sqlunit] Tearing down test... [sqlunit] . [sqlunit] Time: 1.204 [sqlunit] [sqlunit] OK (1 tests) [sqlunit] BUILD SUCCESSFUL Total time: 2 seconds
(1) The error message says that there is a difference in resultsetId = 1, rowId = 2, colId = 4 (2) This is what the test expected. This is supplied to SQLUnit by the test author in the XML file. (4) This is the result object that SQLUnit generated by running the SQL or stored procedure. (3) This is the place in the expected result where the difference was detected. (5) This is the place in the generated result which is different from the expected result. This looks like a simple typo, so our corrective action would be to correct the test specification. Other kinds of errors may require us to fix the stored procedure code and re-run the test.
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]
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.
<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.
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:
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:
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.
compile: [javac] Compiling 10 source files to /home/sujit/src/sqlunit/build compile-test: [javac] Compiling 31 source files to /home/sujit/src/sqlunit/build def: test: init: precompile: compile: [javac] Compiling 10 source files to /home/sujit/src/sqlunit/build canoo2html: [xslt] Processing /home/sujit/src/sqlunit/output/mytests.xml to /home/sujit/src/sqlunit/output/mytests.xml.html [xslt] Loading stylesheet /home/sujit/src/sqlunit/etc/canoo2html_result_transform.xsl BUILD SUCCESSFUL Total time: 10 seconds
The first target will run SQLUnit against the test/mock/coretests.xml file, and generate a Canoo XML test log in the output directory. The target need not necessarily be sqlunitflat, it could also be sqlunit-nested or test from the distribution, or your own custom target to run the tests. The second target applies an XSL transformation to the Canoo XML file to produce an XHTML file in the output directory (the same directory as the Canoo XML file). The XHTML file name is the same as the Canoo XML file name suffixed with .html. Thus, in our case described above, the XHTML file is output/mytests.xml.html. The tests are color coded in the XHTML file, successful runs are shown in green and failures in red.
SQLUnit Tags
List of currently supported tags
The XML Tags that make up a SQLUnit test specification are described below.
Syntax
sqlunit ::= ((connection)+, (setup)*, (test | batchtest | diff | echo | func)+, (teardown)*)
Attributes None
Nested Elements Table 1. Nested Elements for sqlunit Name connection setup test Description Specifies one or more database Connections that need to be instantiated for the test to run. Specifies any setup tasks that need to happen before all the tests in the suite are run. Specifies a test that needs to run in this suite. Required Yes, one or more. No Either one or more of test, batchtest, diff, echo
Name
Description or func
Required
batchtest
Specifies a test that uses the JDBC batching functionality. Specifies a test that compares the results generated by two SQL or stored procedure calls
Either one or more of test, batchtest, diff, echo or func Either one or more of test, batchtest, diff, echo or func Either one or more of test, batchtest, diff, echo or func Either one or more of test, batchtest, diff, echo or func No
diff
echo
Echoes a string after substitution to the log. Populates a single value returned from a stored procedure or SQL call into the SymbolTable, identified by function name Specifies any tasks that need to happen after all the tests in the suite are run
func
teardown
Description The setup tag specifies operations that need to be carried out once before all the tests defined in the XML file. Typical tasks may include dropping and recreating the test database and tables, or deleting and inserting test data into the tables. A setup operation can be specified either by zero or more nested sql tags or by zero or more nested include tag which specify an external file containing the setup SQL to execute. It can also contain set tags to setup variables to be used later for the test, or foreach tags which can be used to execute a single SQL multiple times, such as insert SQL statements.
Syntax
setup ::= ((set)*, (((sql)*, (foreach)*) | (include)*))
Parent Elements
sqlunit
Attributes None
Nested Elements Table 2. Nested Elements for setup Name set funcdef subdef sql foreach include Description Sets initial values for SQLUnit variables. Defines a SQL or stored procedure call that can be used like a function call (single string result). Defines the structure of a SQL or stored procedure call. Values of parameters can be supplied or overriden in the actual sub call. Specifies the SQL statement to be executed with or without replaceable parameters. Contains bulk SQL statements, such as inserts which are executed within the foreach tag. Specifies an external file which contains the SQL statements to execute during the setup phase. Required No No No No No No
Description
The skip tag is used to indicate to SQLUnit that this test should be skipped.
Syntax
skip ::= (BODY)
value If set to true, then the test will be skipped No, default is false
Description The param tag is used to specify an argument to a SQL statement or stored procedure, or aggregated into a set of paramset tags for batch calls. The body of the tag contains the value of the parameter.
Syntax
subparam ::= (BODY)
Attributes Table 4. Attributes for subparam Name id Description Specifies the sequence number of the parameter to be replaced. The sequence is one-based to keep parity with the JDBC specification. Required Yes
Specifies the name of the parameter to be replaced. This has been name added for readability to help stored procedure authors/testers to spot errors quickly.
No
type
Specifies the type of the parameter to be replaced. The type is a String, and has a one to one mapping to the datatypes defined in java.sql.Types. It also allows some additional Oracle specific types specified in net.sourceforge.sqlunit.OracleExtensionTypes. The value Yes of the type is the same as the field name of the member variables in Types or OracleExtensionTypes classes. Thus if the type is Integer, the corresponding SQL type is java.sql.Type.INTEGER, and the string value that should be used here is INTEGER. If set to true, indicates that this parameter is an SQL NULL value. Valid values are true or false. No, default is false.
isnull
inout
No, Specifies whether the parameter is an IN, OUT or INOUT parameter. default is Valid values are in, out and inout respectively. in.
Examples
Description The batchresult element specifies the expected result from running either the statements specified in a batchsql or batchcall tag
Syntax
batchresult ::= ((updatecount)*)
Attributes Table 5. Attributes for batchresult Name expectedcount Description Specifies the number of rows expected to be updated as a result of executing the batchsql or batchcall element. If the expectedcount is specified, then the number of updatecount elements will not be counted for determining the test's success Specifies which statement in the batch is expected to fail when running the test. The index is zero-based Required
No
failed-at
No
Nested Elements
Table 6. Nested Elements for batchresult Name Description Required Yes, unless either or both expected-count and failed-at are specified
Specifies in the body of the element the number of rows expected to be modified by the SQL at updatecount the corresponding position (zero-based) in the batch
Description The call tag is used to describe a call to a stored procedure or function. A stored procedure differs from a standard SQL call in that it can return multiple result sets in a single result. Not all databases support this functionality in the same way. Stored procedures are called within Java code using the standard call syntax that looks like {call [?=]stored_procedure_name([?,...])}. This is also the way it should be specified to SQLUnit. SQLUnit uses the CallableStatement to execute this call
Syntax
call ::= (stmt, (param)*)
Attributes Table 7. Attributes for call Name id Description An internal id for the stored procedure call Required No No
Used to specify a particular connection when multiple connectionConnections are specified in a given test. If not specified, id SQLUnit will try to use the default Connection, if one exists
Nested Elements Table 8. Nested Elements for call Name stmt param Description Required
Specifies the actual stored procedure call in JDBC format described Yes above in the body of this element A param element specifies a single replaceable parameter specified for the stored procedure call. Zero or more
Description
The set tag allows declarations of variable names and associating them with values within the context of a single SQLUnit run. SQLUnit stores these variables and their values in an internal symbol table. Variables need not be pre-declared in order to be used. A variable is usually created in the symbol table by being assigned the appropriate value based on context, the first time it is referenced in the XML file. Subsequent references to the variable will return the value of the variable.
Syntax
set ::= ((sql|call),result)?|(constructoryArgs*,methodArgs*)?
Attributes Table 9. Attributes for set Name Description Specifies the name of the SQLUnit variable. A variable must always be specified like ${varname}. Attempts to specify Yes variables like varname or $varname will result in syntax error. Specifies the initial value of the variable. The value must be specified. In cases where you do not know the value, you can Not required if initialize it with a default value, or not invoke the set tag to nested elements declare the variable. When the variable is first encountered in are specified. SQLUnit, it will implicitly create an entry for that variable in the symbol table. Specifies whether the method call is static. Valid values are true and false. Required if the class and method attributes are also supplied Only required if the method attribute is also supplied Required
name
value
static
class
Specifies the class name whose method has to be invoked to populate the variable
Name
Description
Nested Elements Table 10. Nested Elements for set Name Description Specifies a SQL statement to invoke which will populate the variables in the result. Required Only required if there is no value attribute in the set tag. A call tag can also be specified here instead.
sql
call
Only required if there is no Specifies a stored procedure call to value attribute in the set tag. invoke which will populate the variables A sql tag can also be in the result. specified instead. Specifies a result tag with variables in the col tag body. The variables will be populated from the values returned by the SQL or stored procedure call. Only required if there is either a sql or a call tag.
result
Specifies arguments to a constructor of a Only required if we want to constructorArgs non-static method if that is being used to invoke a non-static method populate the variable to set. methodArgs Specifies arguments to the method to be executed to populate the variable to be set Required if we want to invoke a method to set the variable
<result> <resultset id="1"> <row id="1"> <col id="1" name="c1" type="INTEGER">${col1}</col> </row> </resultset> </result> </set>
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)?)
Attributes
None
Description A wrapper tag to group together JNDI properties that are needed to instantiate an InitialContext for the JNDI DataSource lookup.
Syntax
jndi ::= ((arg)*)
Attributes None
Nested Elements Table 11. Nested Elements for jndi Name arg Description A tag that specifies the name and value of a single JNDI property. Required One or more arg elements are required.
Description The methodinvoker tag allows the caller to invoke methods in Java code and check to see if they complete without exception. These methods are generally, but need not be, JDBC specific. Any objects that are returned from the methods are ignored. The tag only verifies that the method ran successfully, and if not, it threw an expected exception.
Syntax
methodinvoker ::= ((constructorArgs)*, (methodArgs)*)
Attributes Table 12. Attributes for methodinvoker Name static class Description If set to true, specifies that the method being invoked is static. Valid values are true and false. Specifies the full class name of the class in which the method is defined. Required No, default is false. Yes Yes
Nested Elements Table 13. Nested Elements for methodinvoker Name Description Required
Wraps a set of arg elements which Not required for static methods, constructorArgs specify the arguments, if any, that the or if the class has a default noconstructor will need. args constructor. methodArgs Wraps a set of arg elements which Not required if the method takes specify the arguments, if any, that the no arguments. method will need.
method="toString" > <constructorArgs> <arg name="c7" type="java.lang.Double.TYPE" value="0.2" /> </constructorArgs> </methodinvoker>
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)*)
Attributes None
Nested Elements Table 14. Nested Elements for teardown Name sql Description Specifies the SQL statements to be executed, with or without replaceable parameters, that should executed on teardown. Required No
Description Contains bulk SQL statements such as SQL DELETE statements, which will be executed from within a foreach tag. Specifies an external file which contains the SQL statements to be executed on teardown.
Required No No
Description The foreach tag allows for simple bulk substitution by replacing a named parameter in a SQL statement with values from a supplied list or values generated by incrementing a counter. The syntax is similar to FOR and FOREACH statements found in most programming languages.
Syntax
foreach ::= ((sql)*)
Attributes
The name of the parameter that will be substituted in the SQL statement. For example, if the param is id, param Yes then all occurences of ${id} will be substituted by the current value of id. A comma-separated list of values for the id. A value set can be specified within each element in the list. The elements of the set are separated with semivalues colons. They are referred to later in the scope of the foreach tag as value_of_param.index. The index is 0based. Specifies the starting value of the sequence of values for the param No, either this or (start,stop,(step)) or (start,count,(step)) may be specified No, values can be specified instead. If neither is specified, then start defaults to 0. No, count can be specified instead. No, defaults to 1. No, stop can be specified instead.
start
The ending value of the sequence of values for param The increment if a sequence is specified with start and stop The number of entries in the sequence specified by start and stop.
Nested Elements Table 16. Nested Elements for foreach Name sql Description Contains a single SQL statement with placeholders for param which will be executed in a loop specified by the foreach attributes. The replaceable placeholder(s) should be specified as ${id}. Required Yes
<foreach param="id" start="0" stop="10" step="1"> <sql> <stmt>insert into foreachtest (id,name) values (${id},'name${id}')</stmt> </sql> </foreach> </prepare>
Description Supplies information for the SQLUnit test harness to connect to the underlying database containing the stored procedures to be tested. Connections can be built using JDBC or from a DataSource object looked up through JNDI. Connections can also be specified using file URLs. Multiple connection tags can be specified if more than one Connection is needed for a single SQLUnit test suite.
Syntax
connection ::= (((driver, url, user, password, (jarfile-url)?) | (datasource, jndi))?)
Attributes Table 17. Attributes for connection Name Description Required No, default Connection is used if not supplied
When multiple Connections are defined in a SQLUnit test, connection- this attribute refers to a Connection to use for running this id call by its connection-id. If not specified, SQLUnit will try to look up the default Connection which has no connection-
Description
Required
extern
If specified, the value of this attribute points to a Java properties file which contains the information necessary to build the Connection. The file can be specified without path information if it can be found in the caller's CLASSPATH No or specified as a relative path, as well as an absolute path on the file system. A sample properties file can be found in /cvs/test/postgresql/sqlunit.properties.
Indicates whether transaction support is provided by SQLUnit. Setting this to on (the default) will make SQLUnit treat each SQL or CALL as a single unit of work. Turning this to off will make SQLUnit not do any COMMITs or ROLLBACKs, it is left to the client to transaction- enforce boundaries of the unit of work by putting COMMIT No, defaults support and ROLLBACK in the SQL or stored procedures. Finally, to on turning this to implicit will put SQLUnit in auto commit mode, but SQLUnit will not do any explicit COMMIT or ROLLBACK. Transaction boundaries will either by enforced by the database if it supports it, or by the JDBC autocommit mechanism if not. reconnecton-failure Will destroy the current Connection and rebuild it in the event of a test failure or exception. This is needed to work around buggy database drivers which leave the Connection in an inconsistent state in such cases. No, defaults to false No, defaults to the database server name
If specified, will override the database server name that is derived from the DatabaseMetaData for the connection. server-name This is used to lookup data types supported by this database.
Nested Elements Table 18. Nested Elements for connection Name driver url user Description Specifies the full Java class name for the JDBC driver. This is needed for building the Connection using JDBC. Specifies the JDBC URL to the database Specifies the database user who will connect to the database Required Yes (JDBC) Yes (JDBC) Yes
Name
Description
Required (JDBC)
password jarfile-url
Specifies the password for the database user Specifies a URL (eg. file:///tmp/mydriver.jar) for a JAR file containing a JDBC driver which is not in the CLASSPATH.
Specifies the name of a DataSource. By convention, this would look something like jdbc:/myDSN, where the DataSource object datasource is actually stored under java:/comp/env/jdbc. This is likely to vary between JNDI servers and sites, so check with your administrator for the DataSource name to use
Yes (JNDI)
jndi
Empty tag, contains zero or more arg elements containing the name-value pairs to pass in when creating the Initial Naming Context that will be used to look up the DataSource object. These Yes name-value pairs locate the JNDI service. Each arg element will (JNDI) correspond to a line in the jndi.properties file for the given JNDI server.
Description The func tag will replace the positional parameters in the SQL string defined in the corresponding funcdef tag, then run it and return a result.
Syntax
func ::= (EMPTY)
Attributes Table 19. Attributes for func Name Description The name of the func instance. The value returned from the execution of the func element will be available in the SymbolTable as ${func.name_of_func} for later tests in the same suite. The name of the funcdef element to look up. Required
name
Yes
lookup
Yes No
connection- The id of the connection to use, when multiple connections are id defined. If not specified, it will use the default connection.
param0-9
The value for ${0}-${9} in the SQL string, if it exists. Note that if the variable is a string, you must supply the enclosing single No quotes to indicate that. This type of query has no indication of the type of parameter being passed to it. The name or number of the sql call No
id
Nested Elements
Table 20. Nested Elements for func Name skip Description Indicates whether the func should be skipped or not. Required No No
Allows user to provide classification criteria for the func which classifiers SQLUnit can use to decide whether it should run the func or not based on criteria provided to match the classifier.
Description Echoes the specified value to the log. This is mainly used for debugging a test.
Syntax
echo ::= EMPTY
Attributes Table 21. Attributes for echo Name Description Required Yes, required by the SQLUnit logger Yes no
name A name for this echo tag text The text to echo
Specifies the expected value of text, if used in a test context. value Saves caller the trouble of having to manually compare the expected and actual returned values.
Description The include tag is used to specify an external file that contains a list of SQL statements that should be executed together. The statements can be separated from one another by a free standing slash (/) or semi-colon character (;) on its own line, or by a line terminated by a semi-colon (;) character.
Syntax
include ::= (EMPTY)
Attributes Table 22. Attributes for include Name file Description Specifies a relative or absolute path name to the file containing the SQL statements. Required Yes
When multiple Connections are defined in a SQLUnit test, this connection- attribute is used to select the Connection to use for running the id SQL statements. If not specified, SQLUnit will use the default Connection if that is specified.
No
Description The result tag allows the test author to specify an expected result from a SQL call or stored procedure. SQLUnit executes the SQL or stored procedure and converts the results into an internal Result object. It will also convert the data supplied in the result tag to an internal Result object, and then compares the two for equality. Values specified in the col nested element of a result element can contain variable names instead of literal values. The variable names will be updated with the values from the internal symbol table. Result tags can also contain an exception element if the call is expected to throw an exception for this particular test. Note that all id attributes of the result and its nested elements start at 1, so as to be consistent with how JDBC resultsets are set up, and minimize confusion between the two representations.
Syntax
result ::= ((((outparam)*, (updatecount)?, (resultset)*)+ | ((outparam)*, exception))?)
Attributes Table 23. Attributes for result Name id The id of the result If set to true, the result will be echoed to the output. The test will be short-circuited and SQLUnit will not attempt to do any matching. This is useful for debugging. Valid values are true and false. Description Required No No, defaults to false
echo
Nested Elements Table 24. Nested Elements for result Name Description Zero or more outparam elements may be specified, each representing an output parameter from the stored procedure. An outparam element can contain a resultset element in case of Oracle CURSOR types, or text information in case of non-CURSOR types. Specifies the updatecount that is returned from the SQL or stored procedure call. Required
outparam
No
updatecount
No.
resultset
The resultset tag specifies a single resultset returned from No. a database call (SQL or stored procedure). A result element can contain one or more resultsets, which in turn
Name
Description will contain one or more rows, which would contain one or more columns. If there are no rows, a resultset can also be empty.
Required
exception
No. Exception The exception tag is used to test for expected failures of a tags can coexist stored procedure in response to specific inputs. with outparam tags.
</result>
Syntax
methodArgs ::= ((arg)*)
Attributes None
Nested Elements Table 25. Nested Elements for methodArgs Name arg Description A tag that specifies the name, type and value of the method argument. Required One or more arg elements are required.
<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>
Description Represents a row in a database table returned from a SQL or stored procedure call.
Syntax
row ::= (col)+
Attributes Table 26. Attributes for row Name id The row id Description Required Yes No, defaults to false
If true, indicates that child cols are partially specified. Valid partial values are true and false. Columns will be matched based on the col element's id attribute.
Name col
Description
Required
A row can have one or more col elements defined within it. Yes
Description The param tag is used to specify an argument to a SQL statement or stored procedure, or aggregated into a set of paramset tags for batch calls. The body of the tag contains the value of the parameter.
Syntax
param ::= (BODY)
Attributes Table 28. Attributes for param Name id Description Specifies the sequence number of the parameter to be replaced. Required Yes
Name
Description The sequence is one-based to keep parity with the JDBC specification.
Required
name
Specifies the name of the parameter to be replaced. This has been No added for readability to help stored procedure authors/testers to spot errors quickly. Specifies the type of the parameter to be replaced. The type is a String, and has a one to one mapping to the datatypes defined in java.sql.Types. It also allows some additional Oracle specific types specified in net.sourceforge.sqlunit.OracleExtensionTypes. The value of the type is the same as the field name of the member Yes variables in Types or OracleExtensionTypes classes. Thus if the type is Integer, the corresponding SQL type is java.sql.Type.INTEGER, and the string value that should be used here is INTEGER. Specifies the scale of the parameter value if the value maps to a BigDecimalType. This is an optional parameter. SQLUnit will try No its best to guess the scale based on the actual value, but this is useful if the value cannot be specified or is NULL. Specifies the name of a user-defined type. This is an optional parameter. No
type
scale
typename
is-null
No, If set to true, indicates that this parameter is an SQL NULL value. default is Valid values are true or false. false. Specifies whether the parameter is an IN, OUT or INOUT parameter. Valid values are in, out and inout respectively. No, default is in.
inout
Description
Syntax
funcdef ::=
Attributes Table 29. Attributes for funcdef Name name query Description The name of the function definition. This will be used to call the definition from the func tag. The SQL query with numbered replaceable parameters (see example below) which will be called to return the result. Required Yes Yes No
<funcdef name="databasedef" query="select DATABASE()" description="Returns the current database name" />
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)+)
When multiple Connections are defined in a SQLUnit test, this attribute provides a mechanism for choosing one of the defined connectionConnections to use for running this batch. If a connection-id is id not specified, SQLUnit will try to look up a Connection which has no connection-id.
No
Nested Elements Table 31. Nested Elements for batchsql Name stmt Description Contains the SQL statement or stored procedure call in the body of the element Required One or more stmt elements need to be specified
Description The prepare tag specifies additional SQL statements that should be run before the test in whose block it appears in. This is used for specifying additional operations that need to happen on a per-test basis, which is not covered in the setup tag.
Syntax
prepare ::= ((set)*, (sql)*, (foreach)*)
Parent Elements
test
Attributes None
Nested Elements Table 32. Nested Elements for prepare Name set sql foreach Description This tag sets values for SQLUnit test variables into the SQLUnit symbol table. Identifies one or more SQL statements that should be executed as part of the per-test prepare Required No. No.
Specifies a block within a foreach block which is executed as part of No. the prepare.
Description Allows caller to define various levels of Severity for a test. Valid values are FATAL, ERROR, WARN, INFO and DEBUG.
Syntax
severity ::= (BODY)
Attributes None
Syntax
paramset ::= ((param)+)
Nested Elements Table 34. Nested Elements for paramset Name param Description Specifies argument names and values to be passed into a batchcall call. Required One or more
Description The subdef tag is used to define a stored procedure or SQL call. All parameters need to be defined, but some parameters may not have values associated with them. These are plugged in when the actual call is made using the sub tag. This allows test writers to save keystrokes and work at a higher level of abstraction than building the tests manually with stored procedures or SQL calls each time.
Syntax
subdef ::= ((param)*)
Attributes Table 35. Attributes for subdef Name name query description type Description The name of the subroutine definition. This will be used by the sub tag to specify the definition to look up. The SQL or stored procedure call with replaceable parameters specified as in the sql or call tags. Required Yes Yes
A user-friendly description of the subroutine definition. Used No for documentation Specifies whether to treat the SQL as a stored procedure or SQL call when called. Valid values are sql and call. No, defaults to sql
Nested Elements Table 36. Nested Elements for subdef Name Description Required
Specifies zero or more parameters that are supplied with the Zero or more subroutine definition. All parameters that the call requires param should be supplied, although some or all the values may remain parameters can be specified unspecified. Unspecified values are denoted with a single '?' character in the text of the param element.
<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>
Description Represents a column of a database table returned from a SQL or stored procedure call.
Syntax
col ::= (BODY)
Attributes Table 37. Attributes for col Name id The column id Description Required Yes No
The XML name of the data type for the column Yes
Nested Elements
None
Description The batchtest tag allows specifying a batch of SQL statements or stored procedure calls that must be executed within a batch. It allows the client to specify the expected number of results, and a point where a failure may be expected. Note that only statements which update data in some way should be specified hre, since the JDBC specification does not allow the returning of resultsets from SQL statements run using the batching feature
Syntax
batchtest ::= ((skip)?, (classifiers)?, (batchcall | batchsql), batchresult)
Attributes Table 38. Attributes for batchtest Name name Description The name of the batch test. THis is used by Yes Required
Name
Required
Specifies a single or comma-separated assertion that must hold true for the batch test. If specified, SQLUnit will print the user-supplied failure message if the test failed If set to true, Java Object Support is enabled for the test
No, defaults to equal if not specified. No, default is no failure message No, default is false
Nested Elements Table 39. Nested Elements for batchtest Name skip Description Indicates whether the func should be skipped or not. Allows user to provide classification criteria for the func which SQLUnit can use to decide whether it should run the func or not based on criteria provided to match the classifier. Specifies the set of batch calls to be made. Either batchcall or batchsql must be specified Specifies the set of batch calls to be made. Either batchcall or batchsql must be specified Required No
classifiers
No
batchcall batchsql
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)*)
Attributes None
Nested Elements Table 40. Nested Elements for constructorArgs Name arg Description Required
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)+)
connection- When multiple Connections are defined for a SQLUnit test, this No
Name id
Description attribute refers to the Connection to use for running this call. If not specified, SQLUnit will try to look up a Connection which has no connection-id attribute specified.
Required
Nested Elements Table 42. Nested Elements for batchcall Name stmt Description Contains the SQL statement or stored procedure to call in the body. Yes At least one needs to be provided Required
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)
Attributes Table 43. Attributes for outparam Name id name type Description The param-id of the outparam parameter. Required Yes
The name of the outparam parameter. This is mainly for readability to No help stored procedure authors/testers to spot errors quickly. Specifies the type name of the parameter. Yes
Nested Elements Table 44. Nested Elements for outparam Name Description Required
This is needed only when the type of the OUT parameter is Not if the type is resultset a CURSOR, and specifies the value of the CURSOR not CURSOR outparam.
Examples
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)?)
Name name
Description Specifies a human-readable name for the test, which will be printed to the log as part Yes of SQLUnit's progress messages. Specifies a single or comma-separated list of assertions that must be true for the test. Allows the caller to supply an error message which should be displayed if the test failed. If set to on, indicates that Java Object Support is turned on. Valid values are on and off.
Required
No, defaults to equal No, if not specified, no user message will be displayed.
expecteddurationmillis
If specified, specifies that SQLUnit should time the test and fail the test if the test does not complete in the window specified +/- a No percentage tolerance if specified, or 10% if not. If specified with expected-duration-millis, this specifies the percentage tolerance that SQLUnit will put on the expected-duration in order to calculate if the test should fail. No, will default to 10 if expected-duration-millis is specified and percentagetolerance is not specified.
percentagetolerance
Nested Elements Table 46. Nested Elements for test Name skip Description Indicates whether the test should be skipped No or not. Allows user to provide classification criteria for the test which SQLUnit can use to decide whether it should run the test or not No based on criteria provided to match the classifier. Specifies zero or match elements that should be applied to match the result returned with that specified. Specifies SQL setup code that must be run on a per-test basis. No Required
classifiers
match
prepare
No
Name sql
Description Specifies the SQL statement that must be run for this test. Specifies a stored procedure that must be run for the test.
Required Either one of sql, call, methodinvoker, dynamicsql or sub Either one of sql, call, methodinvoker, dynamicsql or sub Either one of sql, call, methodinvoker, dynamicsql or sub
call
dynamicsql
Specifies a method which returns a String of Either one of sql, call, dynamic SQL code that should be executed methodinvoker, for this test. dynamicsql or sub Specifies a predefined and partially specified named SQL or stored procedure call Specifies the expected result from the test. Either one of sql, call, methodinvoker, dynamicsql or sub func is required. Yes
sub
result
Examples
<test name="Adding department HR"> <sql> <stmt>select AddDept(?)</stmt> <param id="1" type="VARCHAR">Human Resources</param> </sql> <result> <resultset id="1"> <row id="1"> <col id="1" name="adddept" type="INTEGER"> ${deptId_HR} </col> </row> </resultset> </result> </test>
Description
Syntax
resultset ::= (row)*
Attributes Table 47. Attributes for resultset Name id partial The resultset id If set to true, indicates that rows are partially specified. Valid values are true and false. Rows will be matched based on the row's id attribute. Description Required Yes No, defaults to false No
Specifies the number of rows in the resultset. This is specified rowcount when the test client wants SQLUnit to only match the number of rows in the resultset, rather than the contents themselves. A comma-separated list of column ids on which the rows in the resultset should be sorted. By default, both the resultset returned from the SQL or stored procedure call and the resultset that is specified will be sorted before comparing. The default sorting is by all the specified columns in the order in which they appear. order-by The order-by attribute should be used only if the default sort needs to be overriden, for example, when the SQL or stored procedure returns ordered data. The sort order specified is ASCENDING, unless the column id is prefixed with a negative sign, which will cause it to be sorted in DESCENDING order. Setting order-by to NONE will turn off auto-sorting.
No
Name row
Description
Required
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)*)
Name
Description
Required
If specified, defines the resultset(s) on which the userdefined matching criteria should be applied. Wildcards may No, defaults to resultsetbe applied to specify multiple values using a '*', eg. '3*' or '*'. all resultsets if id Ranges may be specified using an expression such as '1-2', or not specified enumerations using an expression of the form '1,2,3'. row-id If specified, defines the row(s) on which the user-defined No, defaults to matching strategy will be applied. Same wildcarding rules as all if not defined in resultset-id apply. specified If specified, defines the column(s) on which the user-defined No, defaults to matching strategy should be applied. Same wildcarding rules all if not as defined in resultset-id apply. specified The full class name of the Matcher class that will be used for the user-defined matching. The Matcher class must Yes implement the IMatcher interface and be available in the caller's CLASSPATH
col-id
matcher
Nested Elements Table 50. Nested Elements for match Name arg Description Specifies the name and value of the parameters that are needed by the Matcher class, in addition to the actual column values, to compute the match. Required Zero or more arg elements may need to be specified
Description The sub tag calls a partially defined stored procedure or SQL call in a preceding subdef tag.
Syntax
sub ::=
Attributes Table 51. Attributes for sub Name lookup connectionid Description Specifies the name of the subdef element to look up. The connection-id to use or use the default connection if not specified. Required Yes No
Nested Elements Table 52. Nested Elements for sub Name Description Required Zero or more
Specifies a value for the parameter for the stored procedure or subparam SQL call that was declared in the subdef tag. Lookup is by parameter name.
Description Specifies an expected exception with expected error code and message
Syntax
exception ::= (code)?, (message)?
Attributes None
Nested Elements Table 53. Nested Elements for exception Name code Description The error code for the expected exception Required No
Examples
An exception declaration
<exception> <code>0</code> <message>ERROR: Cannot insert a duplicate key into unique index ux2_employee</message> </exception>
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))
Description The name of the test used by SQLUnit to report progress messages in its log output. Specifies a single assertion or a comma-separated list of assertions which must be true for this diff. If specified, SQLUnit will print the user-supplied failure message if the test failed. Yes
Required
If specified, Java Object Support is enabled for the No, default is false test If set to true, runs the calls in parallel to save time No, default is false (serial execution)
Nested Elements Table 55. Nested Elements for diff Name skip Description Indicates whether the diff should be skipped or not. No Required
Allows user to provide classification criteria for the diff which SQLUnit can use to decide classifiers whether it should run the diff or not based on criteria provided to match the classifier. match prepare
No
Provides overrides for the matching strategy on a No per column basis Allows per test setup calls No. Zero or one prepares can be specified. Either one of sql or call needs to be specified. Two and only two needs to be specified Either one of sql or call needs to be specified. Two and only two needs to be specified.
sql
call
Examples
Syntax
category ::= (BODY)
Attributes None
Description Carries argument values and types for the containing element
Syntax
arg ::= (EMPTY)
Attributes Table 56. Attributes for arg Name name Description The name of the argument to be used Yes Yes No, but required in methodArgs and constructorArgs Required
value The value of the argument type The type of the argument
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
Attributes Table 57. Attributes for sql Name id Description The name or number of the sql call Required No No
When multiple Connections are defined in a SQLUnit test, this connectionattribute refers to the Connection to use for running this call. If id not specified, SQLUnit tries to look up the default Connection.
Nested Elements Table 58. Nested Elements for sql Name stmt Description Specifies the actual SQL statement, with or without replaceable parameters in the body of the tag. Occurs once for each replaceable parameter, if specified, for the SQL string. Yes Yes, if there are replaceable parameters in the SQL string. Required
param
Description
The dynamicsql tag invokes a named method in a named class in order to generate a SQL string dynamically, which is then fed to SQLUnit for testing against a known result. This was included because it is often necessary to test dynamic SQL that is built in response to known conditions, and the results are known, but the actual SQL may not be of interest.
Syntax
dynamicsql ::= ((constructorArgs)*, (methodArgs)*)
Attributes Table 59. Attributes for dynamicsql Name static Description Required
No, default is If set to true, specifies that the method is a static method false (non-static method) Specifies the full class name where the method is defined Specifies the method name to invoke The name or number of the sql call Yes Yes No
class method id
When multiple Connections are defined in a SQLUnit connection- test, this attribute refers to the Connection to use for id running this call. If not specified, SQLUnit tries to look up the default Connection.
No
Nested Elements Table 60. Nested Elements for dynamicsql Name Description Required
Name
Description
Required
Wraps a set of arg elements which Not required if this is a static constructorArgs specify the arguments the constructor method, or if the class has a for the class will take default null constructor methodArgs Wraps a set of arg elements which specify the arguments to pass to the method Not required if the method does not take arguments
If you are already familiar with the pre-3.8 code, then you will find many changes in code for releases 4.0 and up. Hopefully, you will find that the changes are for the better. The code is (at least in my opinion) easier to read and understand, and better organized. Most of all, it should be easier to add new features as they are requested. Table 61. SQLUnit Code Layout Package Name Location Description The main package which contains the classes which did not fit neatly into the subpackages detailed below. This used to be the only package for SQLUnit. Contains the Ant SQLUnit task Contains beans to support the handlers. Older versions of SQLUnit handlers would return an Object from the process() method. They still return Objects, but now some of the handlers return well-defined Objects which can be cast to one of the beans in this package.
net.sourceforge.sqlunit
src/net/sourceforge/sqlunit
net.sourceforge.sqlunit.ant
src/net/sourceforge/sqlunit/ant
net.sourceforge.sqlunit.beans
src/net/sourceforge/sqlunit/beans
net.sourceforge.sqlunit.handlers
Package Name
Location
Description All the implementations implement the IHandler interface. Contains implementations of various types. All the implementations implement the IType interface.
net.sourceforge.sqlunit.types
src/net/sourceforge/sqlunit/types
Contains some user-defined matcher classes for use with the net.sourceforge.sqlunit.matchers src/net/sourceforge/sqlunit/matchers diff tag. All matchers implement the IMatcher interface. Contains reporter classes that can be used with SQLUnit. net.sourceforge.sqlunit.reporters src/net/sourceforge/sqlunit/reporters All reporters implement the IReporter interface. Contains utility classes whose methods get called by SQLUnit. Contains tools to generate test cases from existing SQL statements using console input and GUI based input.
net.sourceforge.sqlunit.utils
src/net/sourceforge/sqlunit/utils
net.sourceforge.sqlunit.tools
src/net/sourceforge/sqlunit/tools
Location
Description Contains some Java code used for LOB testing. Contains code for the mock database framework for testing SQLUnit code independent of a database. Contains subdirectories named after various databases and contains stored procedures and test XML files for these databases. Most of SQLUnit is interface driven, and uses Factory classes to instantiate a named implementation of an interface. The registry files contain the mappings to the various implementations.
net.sourceforge.sqlunit.test.mock test/java/mock
test/*
etc/*.properties
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.
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
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>
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>
<connection connection-id="4"> <driver>net.sourceforge.sqlunit.test.mock.SQLUnitMockDriver</driver> <url>jdbc:mock:net.sourceforge.sqlunit.test.mock.SQLUnitMockDatabase</url> <user /> <password /> <jarfile-url>file:build/,file:lib/mockrunner.jar,file:lib/log4j1.2.8.jar,file:lib/commons-lang.jar</jarfile-url> </connection>
To set up an external JNDI connection, the properties file must contain the following name-value pairs:
# # etc/external-jndi-connection.properties # sqlunit.datasource = jdbc/mockDSN sqlunit.jndi.java.naming.factory.initial = net.sourceforge.sqlunit.test.mock.MockInitialContextFactory
The connection element to point to a properties file as a file name would look like this:
<connection connection-id="5" extern="etc/external-jdbcconnection.properties" />
To specify the properties file as a resource, it must be in the classpath and the connection element will look like this:
<connection connection-id="7" extern="external-jdbc-connection" />
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
When transaction-support=on (the default), SQLUnit turns off JDBC AutoCommit mode and ensures that each SQL or CALL is committed on success and rolled back on failure. The unit of work it assumes is the block contained in either an sql.stmt or call.stmt tag. If a different unit of work is required for your tests, you should consider setting transactionsupport=off. When transaction-support=off, SQLUnit turns of JDBC AutoCommit mode. It will not do either COMMIT or ROLLBACK. The COMMIT or ROLLBACK has to be handled by the client by adding the appropriate statements in the stored procedures. When transaction-support=implicit, SQLUnit will turn on JDBC AutoCommit mode, but not issue any COMMITs or ROLLBACKs. The job of controlling transactions is left to the database. Some databases, such as Sybase and MS SQL Server, have modes that intelligently handle transactional units of work on behalf of the client. In situations where the database does not offer these features, the JDBC transaction behavior will be used, where every SQL statement or stored procedure call will be treated as a single transaction. Prior to version 4.6, the transaction support feature did not work correctly. AutoCommit was turned on in both transaction-support on and off modes. The only difference between the two modes was that SQLUnit did a COMMIT or ROLLBACK after executing a SQL or CALL statement in the "on" mode, and not in the "off" mode. The problem was not noticeable unless you explicitly wanted to change the default unit of work, and found that setting transaction-support=off did not produce the results you expected. The fix in version 4.6 was to set the default AutoCommit mode to false, so transaction-support=off would not do any COMMIT or ROLLBACK on its own. However, this created a problem for Sybase tests that use the CHAINED feature (and is likely to cause similar problems for MS SQL Server tests that use TRANSACTION MODE IMPLICIT, although none have been reported at the time of this writing) with transaction-support=off. Using CHAINED or IMPLICIT implies that the caller lets the database decide when to apply a COMMIT or ROLLBACK, if the database supports it, or apply COMMIT or ROLLBACK after every call (the JDBC default). This mode requires that AutoCommit must be turned on. For this scenario, a new transaction-support mode "implicit" has been introduced in version 4.7. If you experience problems with CHAINED mode or IMPLICIT mode in Sybase or MS SQL Server, then you should try to use the transaction-mode="implicit".
Using variables
Setting explicitly from scalar
A variable "var" in a SQLUnit test specification is denoted by ${var}. The value of a single variable can be set explicitly using the scalar form of the set tag:
<set name="${var}" value="41">
</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);
In both the implicit and explicit cases, the variable ${var} functions like an lvalue in an assignment expression. Once the variable is set, the variable functions like an rvalue in an assignment expression. So if we had specified the scalar set tag above to appear in the setup tag for the test specification or the prepare tag for the test, then the test would compare the value that AddDept returned with the value for the variable ${var}.
The scope of the variable is an individual test specification file. So a variable that has been declared and assigned a value is available for the life of the test suite defined by a single XML file. The only way to reset the value of a variable that has already been declared and assigned a value is to invoke the set tag for that variable again within the test specification.
Including Files
Including XML Files
When talking of included files, people commonly mean included XML in the context of XInclude or external entity references. The SQLUnit include tag represents an unfortunate choice of keyword and does not imply inclusion of external XML files. SQLUnit does not natively support inclusion of external XML files at all. If this functionality is desired, then you can use external entity references or embed XInclude tags and pre-process your test suite with a tool such as XIncluder. Using External entity references is simple and requires no pre-processing of the test suite. Suppose you wanted to include an XML snippet that predeclares certain common operations that you wish to have executed during the setup phase of each test. The example below shows the declarations you would need to make.
<!DOCTYPE sqlunit SYSTEM "sqlunit.dtd" [ <!ENTITY common-setup SYSTEM "file:tests/common-setup.xml"> ]> <connection... /> <setup> &common-setup; </setup> <test... />
The ENTITY declaration within the DOCTYPE associates the name of the entity with the file name. The entity can then be referenced in the body of the test suite as shown in the setup tag above. Information on using embedded XInclude tags and using a pre-processor can be found at the XIncluder project. I considered calling XIncluder from within SQLUnit, but that would impose a penalty for everyone, including people who have no interest in including
XML files. If you need this functionality, it is easy enough to pre-process the file using a script, and then delete the generated test suite XML file after the test is complete.
listed above will work with an EXECUTE PROCEDURE statement. Like CREATE and REPLACE statements, EXECUTE PROCEDURE statements can be multi-line. Other SQL statements are those which do not fall into the two categories described above. Examples are SELECT, INSERT, UPDATE, DELETE, INDEX, DROP, etc. They are handled by SQLUnit using PreparedStatement. Any of the listed delimiters will work for these statements. Like the CREATE/REPLACE and EXECUTE PROCEDURE statements, these can also be multi-line. Some examples are listed below:
-- Simple select select * from foo where bar = 1 / -- A multi-line example UPDATE foo SET bar = 1 WHERE bar = 2 /
In all the types of SQL calls described above, case does not matter, unless it is enforced by the database engine, for example, case sensitive table and column names in Sybase. Shell calls are calls to the operating system. They are preceded with a bang ("!") character on a new line. Since Java will spawn a separate process for the shell command, it has no knowledge of the user's environment. It is generally recommended to call a shell script or batch file with the full path name and source the environment within the script. In case you wish to use the shell metacharacters such as the pipe ("|") or redirection metacharacters, it must be called within the context of the shell, with the actual command in parenthesis. As before, the full path to the command processor will need to be supplied. Multi-line commands can be specified using backslash ("\") character. Any of the delimiters listed above can be used. Here are some examples of a shell call.
-- Example of a shell script call !/path/to/shell/script.sh / -- Example of a call using the shell !/bin/sh -c (rm -rf /tmp/*.test; ls -l | wc -l >/tmp/xyz) / -- Example of a call using the Windows NT command processor !COMMAND.COM /C (DEL C:\TMP\*.TST) / -- Example of a call using Windows XP command processor !CMD.EXE /C (DEL C:\TMP\*.TST) /
In all cases, the parser makes no attempt to validate or parse the SQL or shell script. If SQLUnit encounters an error while running the statement, it will return the stack trace to the caller to aid in debugging.
The BNF grammar (generated by jjdoc) for the Include File Parser is shown below. The comments come from inline comments in the parser grammar file, and are terse versions of the description above.
DOCUMENT START NON-TERMINALS // An include file is a collection of statements separated by one of the // available delimiter characters. StatementCollection := ( CallableSQLStatement | ShellCallStatement | OtherSQLStatement | MultilineSQLStatement | CommentStatement )* ( <EOF> )? // Comment statements can either be single line or multiline. CommentStatement := ( SingleLineCommentStatement | MultiLineCommentStatement ) // Single line comments begin with a "--" string on a newline and are // terminated by newline. SingleLineCommentStatement := ( <START_SINGLELINE_COMMENT> ( <SINGLELINE_COMMENT_CHAR> )* <END_SINGLELINE_COMMENT> ) // Multi line comments begin with "/*" on a newline and can contain // multiple lines. They are terminated with a "*/" string at the end // of the line. MultiLineCommentStatement := ( <START_MULTILINE_COMMENT> ( <MULTILINE_COMMENT_CHAR> )* <END_MULTILINE_COMMENT> ) // Multi line SQL statements are those that have a semi-colon (";") character // terminating lines within the multi-line SQL. The trailing semi-colon // delimiter is not allowed for this type of statement. MultilineSQLStatement := ( <START_MULTILINE> ( <MULTILINE_CHAR> )* <MULTILINE_DELIMITER> ) // A Callable SQL statement is one which starts with "EXEC(UTE PROCEDURE) " // or "{CALL ". The execute procedure call will be rewritten to a CALL form, // since that is the form all JDBC drivers understand. The handler will // use a java.sql.CallableStatement to handle this call. CallableSQLStatement := ( ( <START_EXEC_PROC_CALL> | <START_EXEC_PROC_OTHER> ) ( <EXEC_PROC_CHAR> | <EXEC_PROC_OPEN_PAREN> | <EXEC_PROC_CLOSE_PAREN> | <EXEC_PROC_CONT_CHAR> )* <EXEC_PROC_DELIMITER> ) // A Shell call statement represents a call to an operating system command // or external utility. This may be useful for cleaning up temporary files // or making SQL calls through the database client of your choice. A statement // can span multiple lines using the backslash (\) continuation character. // The parser treats the shell call as a system process and will start up a // Java Runtime object for it. Note that the runtime will have no knowledge // of the environment settings, so you need to provide full path names.
Special // features of the shell such as pipes and redirectors will also not work // unless you wrap the command in the shell processor, like so: // !/bin/sh -c (command) for Unix/Linux // !command.com /c (command) for Windows XP // !cmd.exe /c (command) for Windows NT // The parenthesis are required. The recommended procedure would be to // simply invoke a shell script which does what you want as well as source // environment variables that you may need. ShellCallStatement := ( <START_SHELL_CALL> ( <SHELL_CALL_CHAR> | <SHELL_CONT_CHAR> | <OPEN_PAREN> | <CLOSE_PAREN> )* <SHELL_CALL_DELIMITER> ) // The Other SQL statement represents an SQL statement that is not a CREATE // or REPLACE call (MultilineSQLStatement) or an EXEC(UTE PROCEDURE) call // (CallableSQLStatement). SQL statements such as SELECT, INSERT, UPDATE, // DELETE, RENAME, DROP, etc, fall into this category. The SQL statement // can contain newlines which will be transformed by the parser into white // space. The parser will use a java.sql.PreparedStatement to process this // statement. OtherSQLStatement := ( ( <START_OTHER_SQL_CHAR> | <OTHER_SQL_CHAR> )* <OTHER_SQL_DELIMITER> ) DOCUMENT END
Error Messages
The error messages that SQLUnit will generate if there is a problem are listed below, along with possible corrective actions: Table 1. SQLUnit Error Messages Error Message Corrective Action
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
Corrective Action The test file specified could not be found by SQLUnit. Check to see if the test file exists as specified on your operating system The test file name was specified both in the testfile attribute as well as the fileset nested element. Use one or the other, not both SQLUnit does not have sufficient permissions to read the specified test file. Fix the permissions on the file at the operating system level SQLUnit could not build the Connection object with the properties supplied. Verify that the properties for building the Connection are valid. SQLUnit could not find the named class or resource in the specified Context. The Context could be the system classpath, the path specified by the jarfile-url attribute if that is specified, or the Naming Context if Connection is being looked up from a JNDI server. The include file specified in the include tag in either the setup or teardown tags could not be found. Check to see if the file exists at the specified location on your operating system. This is a system error and means that there is a problem with the code. Log a bug against the SQLUnit project with details on how to reproduce it. This is a system error and means that there is a problem with the code. Log a bug against the SQLUnit project with details on how to reproduce it. This error message may be encountered when using a User-defined matcher class in conjunction with the diff tag. A Matcher class always throws this type of exception if it did not get the inputs it expected or if it encounters an unexpected exception at runtime. The exception_message provides
Error Message
Corrective Action more information as to what the problem is. Usually, it can be fixed by passign arguments correctly to the Matcher class. The values for the resultset-id, row-id and col-id attributes to the Match tag can either be specified as exact numbers, a commaseparated enumeration or a range. It can also be omitted altogether or be specified as a *, both of which imply not to match against the particular filter. The error message means that the pattern was incorrectly specified or could not be parsed for some reason. The pattern will need to be modified to conform to the rules described above. Some simple matchers are supplied as part of the SQLUnit package. Matchers can also be written by users in their own package and be referenced from the SQLUnit tests provided the user-written Matcher exists in the user's CLASSPATH. The message indicates that it is not. Modify the CLASSPATH to include the user-defined Matcher class. This is a variation of the standard message reported in case of test failures. This message is reported only by the Diff tag since that is the only tag which allows userdefined matching. In addition to the information reported by the test failure, it also reports the class name of the Matcher class currently being used. Corrective action is to fix the test. Partial match at the specified position could not be done because of the reason specified. The String value supplied in the param element could not be converted to the appropriate class dictated by the datatype mapping for that variable. You will most likely need to provide an override mapping specific to your database, or, if there is no suitable mapping, you/we could write a mapping class to handle this type.
No match on variable at [rset,row,col]=[ {rset},{row},{col}] using class {matcher_class_name} expected: {expected_result} but got {actual_result}
Error Message
Corrective Action
The Object returned from the database is not of the same class that the class mapped to the At {position}, could not convert SQL Type SQL_Type wraps. You will most {java_class_name} returned from database likely need to provide an override mapping to {SQL_Type_Name}({SQL_Type}) for your database, or, if there is no suitable mapping, you/we could write a mapping class to handle this type. The specified datatype is one of the standard java.sql.Types but does not have an explicit mapping to a class. You will most likely need to provide an override mapping for your database based on your knowledge of the type, or, if there is no suitable mapping, you/we could write a mapping class to handle this type. SQLUnit could not find a mapping type for this specified type name. This is a nonstandard (not in java.sql.Types) datatype, for which an override needs to be set in the mapping file for your database in usertypes.properties. If none of the existing mapping classes seem suitable, then you/we may have to write a new mapping class and map this to the datatype. SQLUnit could not find the variable defined in the symbol table. If you do not mind missing variables, then you should set partialOk to true. The named symbol could not be found. Check your logic and verify that you have set the symbol before trying to use it. The named parameter appears in the subdef as unspecified, but was not specified in the corresponding sub. A SQL Statement in the include file referenced by the file name failed with the given error code. The SQL statements are indexed starting at 1.
The symbol could not be found in the symbol table Parameter {parameter_name} is not defined Statement {statement} ({number}) in include file {filename} returned result {error_code}
Variable {variable_name} is invalid, SQLUnit only accepts variables in the should be in the format \${variable_name} format ${variable_name}. Fix the name in
Error Message
The type specified in the param declaration for a stored procedure or SQL statement was incorrect. Fix the type in the XML input file. SQLUnit expects a numeric value at the location indicated, but parsing it from its String value to the appropriate numeric value resulted in a NumberFormatException. The reason will give more information as to why the digestion process failed. Large Objects such as BLOBs and CLOBs are digested and then compared with specified MD5 Checksums of files or the MD5 Checksum itself. If Java Object Support is enabled, and the large object represents a Serializable Java Object, or the datatype of the LOB is JAVA_OBJECT, then the object's toString() method will be called and the results returned. This will usually be encountered when using the methodinvoker or dynamicsql tags, which depend on the results of invoking a specified method in a specified class in the CLASSPATH. The error will identify where the problem is happening, and will also include the error stack trace within the exception. To get the stack trace, rerun the test with debug set to true, and fix the problem that is causing this error to appear. The assertion provided in the assert attribute for this test element is not supported. Please check the documentation for a list of supported assertions. The specified assertion failed. The test took either too little or too much time compared with the specified expected time. If the percent tolerance is provided for the test, then SQLUnit will check for
Assertion {assert} failed ({reason}), expected {expected_result}, but got {actual_result}, {failure_message} Expected to complete in {expectedduration} ms +/- {percent-tolerance}%, but took {actual_duration}ms
Error Message
Corrective Action specified time +/- the tolerance, else it will use a default tolerance of 10%. If it is taking too much time, it is possible that your new implementation is not using all the optimizations that your old implementation was taking advantage of. If it is taking too little time, it is possible that your stored procedure is using better optimizations or not doing everything it should. It could also be that your expected durations are now incorrect because of server-level tuning, or that the JVM is overloaded. There could be other causes for this discrepancy which are not covered here, which may be related to your site.
SQLUnit Tests failed, file {filename}, run: This message is reported at the end of each {total_tests_run}, failures: SQLUnit test file if there was an error or {number_of_failed_tests}, errors: failure running the tests in the file. {number_of_errors_encountered} This message is reported at the end of an SQLUnit run, spanning one or more test One or more SQLUnit Tests failed, see the files, if there was a failure or an error in any {console|logfile} for details one of the tests. The output will describe the exact nature of the failure(s). The entire error message is returned, usually from the JVM. The exception class specifies the Exception class name. Log a bug against the SQLUnit project if the meaning is not clear enough, otherwise take the necessary action to fix the condition that is causing it. Turning on the debug attribute for the sqlunit ant task will also show the stack trace for reporting or for analysis.
Supported Datatypes
List of currently supported datatypes
The Database datatypes supported by SQLUnit are described below. Type classes are the names of the class used by SQLUnit to provide support for that datatype. The Type name is the name of the datatype that is used in the SQLUnit test specification. The Server is the database server for which the datatype is defined. A Server name of Any indicates global support across all databases. The Allow Input, Allows Output and Is Sortable columns indicate whether you can use these datatypes as inputs (in your param element) or as outputs (in your result specifications), and whether you can sort by columns with these datatypes (using the sort-by attribute of the resultset element). Table 1. SQLUnit Supported Datatypes Allo Allow Is ws s Server Sortab Inpu Outp le t ut Micros oft SQL Server Any
Type Class
Type Name
Wrapper for
TextType
NTEXT
No
Yes
No
java.io.InputStrea m java.sql.Clob java.sql.Clob java.sql.Array java.lang.Object n/a n/a n/a n/a n/a java.lang.Integer java.lang.Integer java.io.InputStrea m
CLOB
No No no No No No No No No Yes Yes No
No No no No No No No No No Yes Yes No
LONGVARCHA MySQ R L ARRAY Any Any Any Any Any Any Any Any Sybase ASA Any
JavaObjectType JAVA_OBJECT UnsupportedTy DATALINK pe UnsupportedTy DISTINCT pe UnsupportedTy NULL pe UnsupportedTy REF pe UnsupportedTy STRUCT pe IntegerType IntegerType BinaryType INTEGER SMALLINT BINARY
Type Class
Type Name
Allo Allow Is ws s Server Sortab Inpu Outp le t ut Any Any Any Oracle Any Any Any Any Any Any Any No Yes Yes No Yes Yes No No Yes Yes no Yes Any Any Any Any Any Any No Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes yes Yes Yes Yes Yes Yes Yes Yes Yes Yes No Yes Yes No Yes Yes No No Yes Yes no Yes No Yes Yes Yes Yes Yes Yes Yes
Wrapper for
java.io.InputStrea m java.lang.Byte java.sql.Time java.sql.ResultSet java.sql.Date java.lang.Long byte[] byte[] java.math.BigDec imal java.math.BigDec imal java.sql.Blob java.lang.Short java.lang.Object java.lang.Double java.sql.Timestam p java.lang.Boolean java.lang.Boolean java.lang.String java.lang.String java.lang.String
ByteArrayType BINARY ByteArrayType VARBINARY BigDecimalTyp DECIMAL e BigDecimalTyp NUMERIC e BlobType ShortType OtherType DoubleType TimestampTyp e BooleanType BooleanType StringType StringType StringType BLOB SMALLINT OTHER DOUBLE TIMESTAMP BIT BOOLEAN CHAR
StringType
NCHAR
Yes
Yes
Yes
java.lang.String
Type Class
Type Name
Allo Allow Is ws s Server Sortab Inpu Outp le t ut Micros oft SQL Server Oracle Any
Wrapper for
StringType
NVARCHAR
Yes
Yes
Yes
java.lang.String
StringType FloatType
FIXED_CHAR REAL
Yes Yes
Yes Yes
Yes Yes
java.lang.String java.lang.Float
Starting with version 4.3, the base type mappings have been slightly modified to conform with the usage recommendations from Sun Microsystems. Details of the changes are given below. Table 2. Changes to base type mappings Data Type FLOAT Old Mapping FloatType (java.lang.Float) DoubleType (java.lang.Double) New Mapping DoubleType (java.lang.Double) BigDecimalType (java.math.BigDecimal )
NUMERIC
VARBINAR Y
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.
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:
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.
This will override the mapping for NUMERIC when the current server is PostgreSQL.
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"; }
SQLUnit Assertions
List of currently supported Assertions
Prior to version 4.0, SQLUnit only allowed for matching two results for equality, or for matching specific columns using a matching criterion defined by one or more MatchPattern objects. SQLUnit now allows the caller to specify an assertion that should be satisfied for a given test as an attribute of the test, diff and batchtest tags. The change is backward compatible, so if the assert attribute is not specified, it defaults to "equal" in case of diff and test, and "batch-equal" in case of batchtest. Both these default assert attributes are actually macro-assertions, and are composed of a comma-separated series of assertions. These assertions, along with some new ones, can be specified either individually or in a sequence that makes sense in the context of the test case. The assertions that are currently supported by SQLUnit are shown below:
Table 1. SQLUnit Assertions Assert-String none Description Asserts nothing about the generated and the actual results. This is typically used to disable assertions for the particular test. Asserts that the two results are equal. This is actually a macro assertion, which resolves to a number of subassertions, which are asserted serially. This is the default assert if not specified in a diff or test tag. Used In diff,test
equal
diff,test
Asserts that the two results are not equal. This is an inversion diff,test of the equals assertion. Asserts that the two results have identical exceptions. Asserts that the result returned is NOT an exception Asserts that the two results have the same number of outparam elements. Asserts that the outparams in both the results are equal. Asserts that the update counts in the two results are equal. diff,test diff,test diff,test
number-ofAsserts that the number of resultsets in the two results are resultsets-equal equal. resultsets-equal resultsetvalues-equal resultset-typesequal Asserts that the resultsets are equal, or match if the test contains embedded match tags. Asserts that the columns in the specified and actual resultset have the same values. No assertion is made for types.
Asserts that the columns in the specified and actual resultsets diff,test are of the same type.
fail-with-failure Asserts that the test will fail with the specified error message. diff,test none Asserts nothing about the generated and the actual batch results. This is typically used to disable assertions for the particular test. Asserts that the two batchresults are equal. This is a macro assertion, composed of a sequence of assertions, which are executed serially. This is the default assertion if no assert attribute is supplied for the batchtest tag. diff,test
equal
batchtest
Assert-String not-equal
Description Asserts that the two batchresults are not equal. This is the inverse of the equal assertion. Asserts that the failed-at attribute of the batchresult tag, if specified, points to the point where the result generated by the batchcall or batchsql actually failed.
Used In batchtest
failed-at-equal
batchtest
Asserts that the number of update counts specified in the expected-count- expected batchresult, either as an expected-count attribute, or batchtest equal listed explicitly, is the same as the one in the generated batchresult. updatecountequal fail-with-failure Asserts that the updatecount values are the same for the result specified by batchresult and the result generated by the batchtest call to batchsql or batchcall. Asserts that the test will fail with the user-supplied failure message. batchtest
We then implement the logic to determine if two DatabaseResult objects are in the same ballpark. This involves adding a method and implementing it, like so:
private static void assertInSameBallpark(String failureMessage, DatabaseResult expR, DatabaseResult gotR, List matchPatterns) throws SQLUnitException { // some logic here. If the test fails it should throw an // SQLUnitException with an ASSERT_FAILED message. }
The toString() method must be defined in order for the comparison to work, since SQLUnit compares two objects for equality based on their String representation returned from the toString() method. Note that SQLUnit does not yet (and probably never will) support passing built-in or user-defined objects as arguments to stored procedures to be tested with SQLUnit. The reason is that it is difficult to convert from the String representation to the equivalent
object representation in a generic manner. The approach taken in the PostgreSQL test (AddEmployee.sql) is to pass the parameters for object creation (the x and y coordinates for the Employee.location::POINT) as separate arguments to the stored procedure. The stored procedure is responsible for constructing a POINT object and storing it into the database. This approach allows scaling to more complicated types (such as CIRCLE((x,y),r)) in a trivial manner.
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.
Pattern
Description Specifying a '*' for a given attribute means that SQLUnit will match all corresponding elements represented by this attribute in the result element. It is equivalent to not specifying the attribute at all. Specifying a single number for a given attribute means that SQLUnit will match all corresponding elements having this attribute in the result element. Specifying a range of numbers for a given attribute means that SQLUnit will match all corresponding elements whose attribute falls in the specified range. Specifying an enumeration for a given attribute means that SQLUnit will match all corresponding elements whose attributes match one of the numbers in the enumeration.
Example resultset-id="1" row-id="*" col-id="*" means match all rows and all columns in resultset 1
resultset-id="*" row-id="*" col-id="3" will match the third column in all rows in all resultsets resultset-id="*" row-id="1" col-id="1-3" will match the first, second and third columns in the first row for all resultsets using this matcher. resultset-id="*" row-id="1" col-id="1,2,3" will do the same thing as the above example. resultset-id="*" row-id="1" col-id="1-3,5,6" will match the first, second, third, fifth and sixth columns in the first row for all resultsets using the matcher.
Any combination of range patterns and simple enumeration separated by commas is also supported.
AllOrNothing Matcher
The AllOrNothingMatcher is an Sujit Pal implementation of the IMatcher (spal@users.sourc interface used to define rulesets for eforge.net) matching columns in SQLUnit. This
Matcher Name
Author
Description implementation is of no particular use, except as a template for other useful matchers and for testing. Based on the argument supplied to it via the argument map, it will either always returns true or false for the column being matched. The SignificantDigitsMatcher is an implementation of the IMatcher interface used to compare FLOAT values where you only really care about matching to a certain number of significant digits to the right of the decimal. Especially useful when you're generating your test script by copying and pasting out of a SQL query tool that displays a different number of significant digits than SQLUnit pulls back from the database. Example configuration: <match resultset-id="1" row-id="*" col-id="9-13,15-18" matcher="net.sourceforge.sqlunit.match ers.SignificantDigitsMatcher"> <arg name="signif-digits" value="3" /> </match>
The TypelessMatcher matches column values only from two different SQL or stored procedure calls. This would be useful when the data returned from two Sujit Pal TypelessMatch different databases may have the same (spal@users.sourc None : er String value but the actual datatypes eforge.net) they are implemented as may be different. This is the matcher that is used implicitly when the assertion "resultsetvalues-equal" is called. The RangeMatcher is an implementation of the IMatcher interface used to define Sujit Pal rulesets for matching columns in (spal@users.sourc SQLUnit. This matcher will accept an eforge.net) absolute tolerance value and check to see that the target is within (+/-) tolerance of the source. toleranc e : An absolute toleranc e value.
RangeMatcher
Matcher Name
Author
Description
Argume nts pctoleranc e:A percenta ge toleranc e value. pctoleranc e:A percenta ge toleranc e value.
PercentageRan geMatcher
The PercentageRangeMatcher is an implementation of the IMatcher interface used to define rulesets for Sujit Pal matching columns in SQLUnit. This (spal@users.sourc matcher will accept a percentage eforge.net) tolerance value and check to see that the target is within (+/-) tolerance percent of the source.
The TypelessPercentageMatcher matches columns which may differ by a Chris Watts TypelessPercen specified percentage. This can be useful (c_watts@users.so tageMatcher when comparing numeric values across urceforge.net) data types which have different default precisions.
ForgivingNull Matcher
The ForgivingNullMatcher is an implementation of the IMatcher interface used to compare values you know are either supposed to be NULL (but might be textually represented differently) or are actually equal. Basically searches for the word NULL (case insensitive) or for blank elements or for elements that are equal Strings. Especially useful when you're generating your test script by copying and pasting out of a SQL query tool that displays NULLs differently than SQLUnit pulls back from the database. For example, all None : of these values are treated as equal: <col id="14" name="benchcode" type="INTEGER">[NULL]</col> <col id="14" name="benchcode" type="INTEGER">NULL</col> <col id="14" name="benchcode" type="INTEGER"></col> <col id="14" name="benchcode" type="INTEGER">null</col> Example configuration: <match resultset-id="1" row-id="*" col-id="14" matcher="net.sourceforge.sqlunit.match ers.ForgivingNullMatcher"> </match>
Matcher Name
Author
Description
Argume nts expressi on : A valid JEXL expressi on. Source and target variables in the expressi on are referenc ed by the pseudovariables expected .value and actual.va lue respectiv ely.
ExpressionMat cher
Allows the caller to specify an expression in Apache JEXL (Java Extended Expression Language). The Sujit Pal expression must evaluate to a boolean (spal@users.sourc expression (true or false). The source eforge.net) and target are referenced in the expression using the pseudo-variables expected.value and actual.value.
* $Id: sqlunit-book.xml,v 1.99 2005/07/08 05:02:15 spal Exp $ (1) * $Source: /cvsroot/sqlunit/sqlunit/docs/sqlunit-book.xml,v $ * SQLUnit - a test harness for unit testing database stored procedures.(2) * Copyright (C) 2003 The SQLUnit Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021111307, USA. */ package net.sourceforge.sqlunit.matchers; (3) import import import import java.util.Map; (4) net.sourceforge.sqlunit.IErrorCodes; net.sourceforge.sqlunit.IMatcher; net.sourceforge.sqlunit.SQLUnitException;
/** (5) * The PercentageRangeMatcher is an implementation of the IMatcher interface * used to define rulesets for matching columns in SQLUnit. This matcher * will accept a percentage tolerance value and check to see that the target * is within (+/-) tolerance percent of the source. * Arguments: * pc-tolerance : a percentage tolerance value. * @author Sujit Pal (spal@users.sourceforge.net) * @version $Revision: 1.99 $ */ public class PercentageRangeMatcher implements IMatcher { (6) /** * Default constructor as per contract with IMatcher. */ public PercentageRangeMatcher() {;} /** * Returns true if the value of the target is withing (+/-) a specified * tolerance value of the source. Note that in this case, the source, * target and tolerance must all be numeric values. * @param source the String representing the source to be matched. * @param target the String representing the target to be matched.
* @param args a Map of name value pairs of arguments passed in. */ public boolean isEqual(String source, String target, Map args) throws SQLUnitException { (7) String aTolerance = (String) args.get("pc-tolerance"); (8) if (aTolerance == null) { throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION, new String[] {this.getClass().getName(), "Value for key 'pc-tolerance' is NULL"}); } // is tolerance a float? float iTolerance = 0; try { iTolerance = Float.parseFloat(aTolerance); } catch (NumberFormatException e) { throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION, new String[] {this.getClass().getName(), "Value of key 'pc-tolerance' is not a FLOAT"}); } // cannot have the tolerance exceed 100 if (iTolerance > 100.0) { throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION, new String[] {this.getClass().getName(), "Value of key 'pc-tolerance' must be between 0 and 100"}); } // is the source a float? (9) float iSource = 0; try { iSource = Float.parseFloat(source); } catch (NumberFormatException e) { throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION, new String[] {this.getClass().getName(), "Value of 'source' is not a FLOAT"}); } // is the target an integer? float iTarget = 0; try { iTarget = Float.parseFloat(target); } catch (NumberFormatException e) { throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION, new String[] {this.getClass().getName(), "Value of 'target' is not a FLOAT"}); } // return the match (10) return ((iTarget >= (iSource - (iTolerance * 100))) && (iTarget <= (iSource + (iTolerance * 100)))); } }
(1) In general, if you are contributing a matcher to the SQLUnit project, please include the Id and Source CVS tags. This will allow us to track version revisions in SQLUnit. Even if you dont plan to contribute the matcher to the SQLUnit
project, its a good practice to include the tags for whatever Source Code Control System you are using. (2) Please include this boilerplate in your code if you are planning to contribute the matcher to the SQLUnit project. This indicates that this code is now covered by the GNU General Public License. If you are not planning to contribute the Matcher, please ignore this callout. (3) The package statement identifies the package in which your matcher class will live in. It can be anything you want, but your CLASSPATH must contain this package when running SQLUnit with your new user-defined Matcher class. (4) You will need to import the following classes from the net.sourceforge.sqlunit package. IMatcher is the interface which your matcher class is implementing. SQLUnitException is the Exception class thrown by SQLUnit applications, and IErrorCodes is another interface which contains the definition of the Error Codes thrown by SQLUnit. It is important to throw SQLUnitExceptions from your Matcher class because otherwise your exceptions will not be reported by SQLUnit. (5) Because of the weak coupling between any Matcher and the rest of SQLUnit, the class documentation is probably the best place you have to provide the user of your Matcher class with useful information relating to the use of the Matcher. The class documentation in this case defines what the Matcher does, and provides the information about what keys need to be passed in to the Matcher for it to do its work. (6) Your Matcher class needs to implement the net.sourceforge.sqlunit.IMatcher interface, otherwise it will not be visible to SQLUnit. (7) Your Matcher class needs to implement the only method in the IMatcher interface, which is the boolean isEqual(String,String,Map). The first argument represents the source column value to match, the second the target column value, and the third is a Map of key-value pairs which contain additional information needed by the isEqual method. If the actual comparison needs to be made on numeric objects, as is the case with the PercentageRangeMatcher, then your Matcher should convert to the appropriate class. (8) The weak coupling between the SQLUnit code and the Matcher code is by design. It was necessiated by the need to make the Matchers be very flexible and to keep the SQLUnit XML code simple to use. As a result, the Matcher must make sure that it can use the arguments being passed to it. If not, it should abort with a SQLUnitException (IErrorCodes.MATCHER_EXCEPTION) specifying the class name and the Matcher specific exception message. (9)
As mentioned above, the Matcher code is responsible for converting from the supplied String value to whatever form it needs. In this case, it converts the source and target String values to Floats. If conversion fails, then it should throw a SQLUnitException (IErrorCodes.MATCHER_EXCEPTION) specifying the class name and the Matcher specific exception message. (10) This computes the value of the match and returns it.
The above example illustrates some rudimentary wildcarding features that SQLUnit supports in order to specify more than one column for a particular matching strategy. Any columns that are not covered by the wildcarded filters default to the default matching strategy, which is to check for exact equality. The rules for wildcarding are the same for the resultset-id, row-id and col-id attributes and are detailed below. SQLUnit decides which columns qualify by checking the patterns for resultset-id, row-id and col-id additively.
User-Defined Reporting
What is User-Defined Reporting?
Starting with version 3.6, SQLUnit has the ability to use reporters that work within the context of other testing frameworks. By default, SQLUnit uses the TextReporter, which can be used to print a text report of the results of the test, either to a file specified in the sqlunit task's logfile attribute, or to STDERR if the logfile attribute is not specified. This functionality was contributed by Rob Nielsen and Paul King. They have also contributed a reporter that works within the context of the Canoo Web Test framework. If you have written a Reporter that does something unique, and would like to contribute it to the SQLUnit project, it would be gratefully accepted and credit given.
CanooWebTestReporter
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" />
</result> </test>
This means that this test belongs to TestForModuleA category and its severity is WARN, so that if it fails it will be just a warning and not a error or a fatal situation. The user then should provide some global criteria and each test's classification will be compared. If the test matches them it will be executed; if not will be skipped and reported as skipped. Here is a detailed explantion how the decision whether a test matches ot not is done. When <classifiers> tag is reached, its corresponding ClassifiersHandler is called, whose in turn calls the handlers of all its children. It is responsibility of the children's handler to extract the criterion they are interested in from the environment and then to compare whether their content meets that criterion. If it is not met they return false (as a Boolean object) to ClassifiersHandler. On the other hand, the latter stops its execution on the first false value it receives and the test is not executed. For a concrete example how the user defines these global criteria see the next two sections. SQLUnit comes with two classifying handlers (but you can provide your own too), which are SeverityHandler and CategoryHandler and the next two sections are dedicated to them.
SeverityHandler
SeverityHandler corresponds to <severity> tag, nested in <classifiers> tag. As shown in the example of the previous section the body of <severity> tag contains the severity of the test. Now the user gives the global severity, which we will call the threshold severity in one of the following way: as an ant property called sqlunit.threshold.severity, as the value of the key sqlunit.threshold.severity from SQLUnit's SymbolTable or as a System property called sqlunit.threshold.severity. SeverityHandler will take the threshold severity, the current test's severity and will execute the test iff the current test's severity is equal ot greater than the threshold severity. The following values are valid severity value: DEBUG, INFO, WARN, ERROR, FATAL and are compared in the following way: DEBUG < INFO < WARN < ERROR < FATAL. For example if the user invokes the test from the previous section with
ant -Dsqlunit.severity.threshold=INFO
the test will be executed because INFO <= WARN. But if it is invoked with
ant -Dsqlunit.severity.threshold=ERROR
the test will not be executed as ERROR > WARN. Since <severity> is an optional tag, the following edge cases are taken into account: if the threshold severity is not set all tests match regardless of their severities; if a test does not have assigned severity it matches, regardless of the threshold severity.
CategoryHandler
CategoryHandler corresponds to <category> tag, nested in <classifiers> tag. As shown in the example of the previous section the body of <category> tag contains the category of the test. Now the user gives the global category in one of the following ways: as an ant property called sqlunit.category, as the value of the key sqlunit.category from SQLUnit's SymbolTable or as a System property called sqlunit.category. CategoryHandler will take the global category, the current test's category and will execute the test iff they match in the terms of regular expression matching. For example if the user invokes the test from the previous section with
ant -Dsqlunit.category=TestForModuleA
the test will 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(.*)
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.
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.
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.
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.