Test XQuery: select and sort books

A catalogue contains published books and drafts. An export should include only published books, sorted by title. This is a typical small XQuery task and a useful candidate for a test.

The function receives the catalogue as a parameter. It does not need an XML database or external service. That lets us check selection and sorting before dealing with credentials or changing server content.

The module and input data

Here is the XQuery module:

examples/05-xquery/catalog.xqm

xquery version "3.1";
module namespace cat = "urn:example:catalog";

declare function cat:published(
  $catalog as element(catalog)
) as element(book)* {
  for $book in $catalog/book[@status = "published"]
  order by string($book/title) collation
    "http://www.w3.org/2005/xpath-functions/collation/codepoint"
  return $book
};

The function accepts a catalog element and returns a sequence of book elements. The explicit codepoint collation fixes the ordering instead of letting an incidental language setting decide it.

The test data deliberately contains a draft, and the published books are not yet in the expected order:

examples/05-xquery/catalog.xml

<catalog>
  <book id="b2" status="published"><title>XML Workflows</title></book>
  <book id="b3" status="draft"><title>Unfinished Book</title></book>
  <book id="b1" status="published"><title>Publishing Systems</title></book>
</catalog>

Describe the call and expected books

In the XSpec description, query identifies the module namespace and query-at identifies its location. They serve different purposes: query is not the file path.

examples/05-xquery/catalog.xspec

<?xml version="1.0" encoding="UTF-8"?>
<x:description xmlns:x="http://www.jenitennison.com/xslt/xspec"
  xmlns:cat="urn:example:catalog" query="urn:example:catalog"
  query-at="catalog.xqm">
  <x:scenario label="Select and sort published books">
    <x:call function="cat:published">
      <x:param href="catalog.xml" select="/catalog"/>
    </x:call>
    <x:expect label="Return only published books in title order"
      href="published.xml" select="/catalog/book"/>
  </x:scenario>
  <x:scenario label="Handle an empty catalog">
    <x:call function="cat:published">
      <x:param select="catalog"><catalog/></x:param>
    </x:call>
    <x:expect label="Return an empty sequence" select="()"/>
  </x:scenario>
</x:description>

The expected file contains only the two published books, already in the right order:

examples/05-xquery/published.xml

The expectation selects the book elements because that sequence is what the function returns. A second scenario tests an empty catalogue. It expects (), not an empty catalog element and not an empty string. That difference matters, for example, when the caller counts the results.

Run the tests

Using the package runner:

python scripts/run-tests.py --only xquery

A direct XSpec invocation uses -q for XQuery mode. The standard setup in this package uses Saxon.

"$XSPEC_HOME/bin/xspec.sh" -q -e -j examples/05-xquery/catalog.xspec

These tests check selection and ordering. They do not check whether a database server uses the right indexes, permissions are correct or transactions work. Database-specific extensions also need their actual target processor; a passing Saxon run does not establish that they work elsewhere.

Add separate integration tests with controlled data for those tasks. Keeping them separate makes it easier to distinguish a query bug from a service that is simply unavailable.

The same pattern works for metadata filters, tables of contents, index entries and export selections. Include something that must be excluded alongside the records that should appear in the output.

<< back                                                                                                             next >>