You can either write tests inside the stylesheet (see the simple example below) or store them externally (see the Standalone Tests section below). You can also see the Pre-defined Tests section to write tests that skip the generation step.
The following simple example shows a single test that checks the
result of calling the
eg:square() function with the number parameter set
to 2. The expected result is the number 4.
<test:tests> <test:test> <test:param name="number" select="2" /> <test:expect select="4" /> </test:test> </test:tests> <xsl:function name="eg:square" as="xs:double"> <xsl:param name="number" as="xs:double" /> <xsl:sequence select="$number * $number" /> </xsl:function>
All the testing elements are in the namespace
http://www.jenitennison.com/xslt/unit-test, so that needs
to be declared in your stylesheet. If you want to prevent
namespace declarations for that namespace littering your code, you can
exclude-result-prefixes attribute on
<xsl:stylesheet> as follows:
<xsl:stylesheet version="..." xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ... xmlns:test="http://www.jenitennison.com/xslt/unit-test" exclude-result-prefixes="... test"> ... </xsl:stylesheet>
The tests that are applicable to a particular template or function are
all wrapped in a
<test:tests> element. This is
primarily to make it easy to collapse them all out of the way (in editors
that can do such a thing) so they don't become too distracting when
writing the code. Individual tests are given in
<test:test> elements can have an
id attribute to specify a unique identifier and/or a
<test:title> child that gives a human-readable
title. Both are optional, however if you use descriptive verb phrases,
you could use these to uniquely identify and describe your tests.
What's more is you can then report on this and present a meaningful
report of what's being tested.
<test:test> element, you need to
specify the input to the template or function and the expected output. The
input can consist of a context node (for templates only) specified with an
<test:context> element and any number of
parameters specified with
The expected output must be specified with a
The values for the context, parameters and expected result are all
defined in the same ways. For atomic values (strings, numbers and the
like), use the
select attribute, as in the example
previously. If you want to specify nodes, you need to supply a document
from which the nodes can be selected. You can do this either with the
href attribute, which gives a URI for an external
document, or by embedding the document (or document fragment) within the
relevant element. The
select attribute is then used to
select nodes within the document; the path it holds is interpreted from
the root/document node of the document. The default value for the
select attribute is
selects (all) the document element(s).
If you want to test the stylesheet as a whole, the easiest thing to do is to create external input/output files and reference them. Note that the result must be an XML document, so you can't use this to test HTML output.
<test:tests> <test:test> <test:context href="input.xml" /> <test:expect href="output.xml" /> </test:test> </test:tests> <xsl:template match="/"> ... </xsl:template>
You can test with simplified documents by simply embedding the important part within the test itself. The embedded document should include any important ancestors of the relevant element, but doesn't need to include any of the irrelevant parts of the document.
select attribute to pick the nodes you want from the
document. Remember that if your function or template returns something
other than elements, you will need a
attribute on the
<test:expect> element too.
<test:tests> <test:test> <test:title>Empty header cells</test:title> <test:context select="/table/tgroup/thead/row/entry"> <table> <tgroup> <thead> <row> <entry /> </row> </thead> </tgroup> </table> </test:context> <test:expect> <th>Š</th> </test:expect> </test:test> </test:tests> <xsl:template match="thead/row/entry"> ... <xsl:when test="not(*) and not(normalize-space(.))"> <th>Š</th> </xsl:when> ... </xsl:template>
NB. Differences in whitespace-only text nodes between the expected and actual result are ignored in this version. If you want to generate tests that take whitespace-only text nodes into account, you'll need to configure the testing; see below.
It's possible to write tests externally to the template they test. This is useful for example, when you don't want to ship tests along with your templates.
A standalone test suite has a
document element. The
<test:suite> element has
stylesheet, which is a URL (relative
to the test suite document) that points to the stylesheet tested by the
date, which is a
that gives the date/time for the suite, useful for versioning.
<test:suite> element contains one or
<test:tests> elements, which are the same
as described above except that they also contain, immediately after the
<test:title> element if there is one, a
<test:xslt> element. The
<test:xslt> element contains either an
<xsl:template> or a
<xsl:function> element, with
mode attributes to identify the template/function
being tested but no content.
Below is an example standalone test suite for a
utils.xsl stylesheet, last modified at 12:44 on
September 20th 2005. The only test shown is for the
<test:suite stylesheet="utils.xsl" date="2005-09-20T12:44:00"> <test:tests> <test:xslt> <xsl:function name="eg:square" /> </test:xslt> <test:test> <test:param name="number" select="2" /> <test:expect select="4" /> </test:test> ... </test:tests> ... </test:suite>
The standalone test suite can be used as the input to
generate-tests.xsl and the tests will be run on the
extract-tests.xsl stylesheet transforms a
stylesheet that has tests embedded in it into a standalone test suite.
The reverse has not yet been implemented...
You can configure the details of how the testing is carried out, and
in particular how sequences/items/nodes are compared, by creating your
own implementations of the various functions in
test:config attribute on the
<xsl:stylesheet> element, or a
config attribute on the
<test:suite> element in a standalone test suite,
can point to a stylesheet which contains these implementations in order
to override the default behaviour.
By default, the framework compares the expected and actual results on an item-by-item basis. So if you expect
<xs:all> <xs:element ref="foo" /> <xs:element ref="bar" /> </xs:all>
but you get
<xs:all> <xs:element ref="bar" /> <xs:element ref="foo" /> </xs:all>
then the test fails.
In some cases, it might be that you really don't care what order
particular elements appear in, just as long as they're all generated.
In this example, the
appearing in the content of the
element can appear in any order with the same meaning.
To configure the testing package to ignore these ordering
differences, you can create
which overrides the
generate-test-utils.xsl. This ensures that
the sequence of
<xs:element> elements are
sorted by name before being compared, effectively ignoring the original
order in which they appeared.
xsd-config.xsl, include a reference to
it from the
test:config attribute on the
<xsl:styelsheet> document element.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:test="http://www.jenitennison.com/xslt/unit-test" extension-element-prefixes="test" test:config="xsd-config.xsl"> ... </xsl:stylesheet>
By writing pre-defined tests, you can avoid the initial step of
generate-test.xsl. Instead you write your tests
directly and import the stylesheet under test along with a utility stylesheet.
You must ensure that;
The stylesheet to test is imported.
tests-utils.xsl template is also imported.
An example is shown below
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:test="http://www.jenitennison.com/xslt/unit-test"> <xsl:import test:stylesheet="" href="./my-stylesheet.xsl"/> <xsl:import href="../tennison-tests/main/src/xslt/tests-utils.xsl"/> <test:suite> <test:tests> <test:test> <test:title>test-initial-transform</test:title> <test:context href="../input1.xml"/> <test:expect href="../output1.xml"/> </test:test> <test:test> <test:title>test-secondary-transform</test:title> <test:context href="../input2.xml"/> <test:expect href="../output2.xml"/> </test:test> </test:tests> </test:suite> </xsl:stylesheet>
This exampe, uses the
inputn.xml as input and compares the
resulting transformation to the output file
Notice that the stylesheet under test is imported (
in this example) and that
test-utils.xsl is imported. This is a
helper that is used to by-pass the generation step, it allows you to
to the root node.