Lets say you have a simple XQuery function that returns the string "Hello World!" like this:

declare function myfunct:hello() as xs:string {
'Hello World!'
};

You want to add a single assertion to the function using an XQuery 3.0 annotation. The XQuery 3.0 Working Draft indicates that annotations always start with the "%" symbol and occur after the keyword "declare" and before the keyword "function" like this:

Note that the %test:assertEquals('Hello World!') has been added to the original function. The "assertion" is just a function that must return true for the test to pass. The output of the function is automatically compared with the input string and in this case the assertEquals function returns true if the strings match exactly.

Note that this syntax is different from other languages like Java that put assertions in comments before each method.

You now have enough information it the function to run a simple unit test.

If you want to test all the functions in a module you can use the util:list-functions($module-uri) function which returns a sequence of function objects for the module.

Sample Test Driver

xquery version "3.0";
(: the following line must be added to each of the modules that include unit tests :)
import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
(: import the module that contains your test-annotated functions :)
import module namespace mt="http://exist-db.org/xquery/test/my-module" at "mymodule.xqm";
(: the test:suite() function will run all the test-annotated functions in the module whose namespace URI you provide :)
test:suite(util:list-functions("http://exist-db.org/xquery/test/my-module"))

Annotations are structures that are added to each function. They are not compiled by the XQuery compiler but can be used by other XQuery scripts to create a database of actual test files to be run. This places the tests directly in the context of each XQuery function. You can use the util:inspect-function() function to get a list of all the annotations in a function.

XUnit is the family name given to a general class of testing frameworks that have become widely known amongst software developers. The name is a derivation of JUnit, the first of these to be widely known.

XUnit also includes a is an standardized XML test results output format that is used by many continious integration tools such as Jenkins, Hudson, CruiseControl, Team City and other tools.

The test name is any string that describes the test. You can add the name of the function to help you know what function is being tested. You can also add a classname attribute to group the test results together in test results reports. The time element is the execution time of the unit test.

We can then write a test driver that will test this function using the assertion:

xquery version "3.0";
(: the following line must be added to each of the modules that include unit tests :)
import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
(: import the module that contains your test-annotated functions :)
import module namespace myfunct="http://exist-db.org/xquery/test/myfunctions" at "mymodule.xqm";
(: the test:suite() function will run all the test-annotated functions in the module whose namespace URI you provide :)
test:suite(util:list-functions("http://exist-db.org/xquery/test/myfunctions"))

The following annotation example shows how you would add an assertion to test a simple function that adds two decimal numbers. Note that you must place the percent sign between the declare and function.

Many XQuery tests can be run based on a single line of code without the need for pre-conditions or post-conditions to the test.

For example %test:assertEquals("Heinz Roland Uschi Verona")

Checks the result returned from the function for equality with the provided data. If the function returned a string sequence with more than one item, a space is added between the items (as above).

An assertion can take any sequence of literals, including strings and numbers, but not XML fragments. However, assertEquals does inspect the return type of the function. If it returned an XML fragment, it will be normalized (ignorable whitespace stripped). The annotation string is parsed into XML as well and the two values are compared using deep-equals(). Thus, if your function returns XML, you provide a string to %test:assertEquals and it is parsed into XML as well.

%test:assertEmpty - returns true if the result is an empty string.

%test:assertExists - returns true if the result exists.

%test:assertTrue - returns true if the result is true

%test:assertFalse - returns true if the result is false

%test:assertError("error code") - Excepts the function to fail with an error. If an error code is given (optional), it should be contained in the error message or the test will fail.

%test:assertXPath("count($result) = 8") - This is the most powerful assertion. It checks the result against an arbitrary XPath expression. The assertion is true if

the XPath results to a boolean true or

the XPath returns a non-empty sequence

The output of the called function is always contained in variable $result.

In this example the first parameter is the $queryStr="fenny snake" and the second parameter is the $mode="all". This example then has two seperate assertions. The first must have at least two results with the class of scene and the section assertion show that the result must contain a table with a row and a table data element with a class of "hi".

You can use the %test:args() function as many times as you want to create multiple tests for a single function. You just repeat the %test:args() function over and over with a separate set of assert statements after each %test:args(). Here is the pattern: