The following stylesheet is meant to be included as a utility. However, this example provides the capability of testing the stylesheet by executing it as its own input document:
<!-- math.max.xslt --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://www.exslt.org/math" exclude-result-prefixes="math" xmlns:test="http://www.ora.com/XSLTCookbook/test" id="math:math.max"> <xsl:template name="math:max"> <xsl:param name="nodes" select="/.."/> <xsl:param name="max"/> <xsl:variable name="count" select="count($nodes)"/> <xsl:variable name="aNode" select="$nodes[ceiling($count div 2)]"/> <xsl:choose> <xsl:when test="not($count)"> <xsl:value-of select="number($max)"/> </xsl:when> <xsl:when test="number($aNode) != number($aNode)"> <xsl:value-of select="number($aNode)"/> </xsl:when> <xsl:otherwise> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="$nodes[not(. <= number($aNode))]"/> <xsl:with-param name="max"> <xsl:choose> <xsl:when test="not($max) or $aNode > $max"> <xsl:value-of select="$aNode"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$max"/> </xsl:otherwise> </xsl:choose> </xsl:with-param> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- TEST CODE: DO NOT REMOVE! --> <xsl:template match="/xsl:stylesheet[@id='math:math.max'] | xsl:include[@href='math. max.xslt'] " priority="-1000"> <xsl:message> TESTING math.max </xsl:message> <xsl:for-each select="document('')/*/test:test"> <xsl:variable name="ans"> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="test:data"/> </xsl:call-template> </xsl:variable> <xsl:if test="$ans != @ans"> <xsl:message> math:max TEST <xsl:value-of select="@num"/> FAILED [<xsl:value-of select="$ans"/>] </xsl:message> </xsl:if> </xsl:for-each> <!-- Test with Infinity --> <xsl:variable name="ans1"> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="document('')/*/test:test[@num=1]/test: data"/> <xsl:with-param name="max" select="1 div 0"/> </xsl:call-template> </xsl:variable> <xsl:if test="$ans1 != Infinity"> <xsl:message> math:max Infinity Test FAILED [<xsl:value-of select="$ans1"/>] </xsl:message> </xsl:if> <!-- Test with -Infinity --> <xsl:variable name="ans2"> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="document('')/*/test:test[@num=1]/test: data"/> <xsl:with-param name="max" select="-1 div 0"/> </xsl:call-template> </xsl:variable> <xsl:if test="$ans2 != document('')/*/test:test[@num=1]/@ans"> <xsl:message> math:max -Infinity Test FAILED [<xsl:value-of select="$ans2"/>] </xsl:message> </xsl:if> </xsl:template> <test:test num="1" ans="9" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>9</data> <data>8</data> <data>7</data> <data>6</data> <data>5</data> <data>4</data> <data>3</data> <data>2</data> <data>1</data> </test:test> <test:test num="2" ans="1" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>1</data> </test:test> <test:test num="3" ans="1" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>-1</data> <data>1</data> </test:test> <test:test num="4" ans="0" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>0</data> <data>0</data> </test:test> <test:test num="5" ans="NaN" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>foo</data> <data>1</data> </test:test> <test:test num="6" ans="NaN" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>1</data> <data>foo</data> </test:test> <test:test num="7" ans="NaN" xmlns="http://www.ora.com/XSLTCookbook/test"> </test:test> </xsl:stylesheet>
The xsl:stylesheet
element has an optional
attribute called id
. This attribute idenfities
stylesheets that are embedded in larger documents. However, here the
ID is used for testing purposes. You want to package test code with
the stylesheet but make reasonably certain that this test code does
not interfere with the normal usage of the stylesheet. Do this by
creating a template that will match only when the stylesheet
processes itself:
<xsl:template match="/xsl:stylesheet[@id='math:math.max'] | xsl:include[@href='math.max.xslt']">
This explains the
/xsl:stylesheet[@id='math:math.max']
, but what
about the xsl:include[@href='math.max.xslt']
part?
To see the value of this, here is a stylesheet that packages all your
math utilities into a single file for easy inclusion. You would like
an easy way to test the entire package too:
<!-- math.xslt --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math" extension-element-prefixes="math" id="math:math"> <xsl:include href="math.abs.xslt"/> <xsl:include href="math.constant.xslt"/> <xsl:include href="math.exp.xslt"/> <xsl:include href="math.highest.xslt"/> <xsl:include href="math.log.xslt"/> <xsl:include href="math.lowest.xslt"/> <xsl:include href="math.max.xslt"/> <xsl:include href="math.min.xslt"/> <xsl:include href="math.power.xslt"/> <xsl:include href="math.sqrt.xslt"/> <!--TEST CODE --> <xsl:template match="xsl:stylesheet[@id='math:math'] | xsl:include[@href='math. xslt']"> <xsl:message> TESTING math </xsl:message> <xsl:for-each select="document('')/*/xsl:include"> <xsl:apply-templates select="."/> </xsl:for-each> </xsl:template> <xsl:template match="xsl:include" priority="-10"> <xsl:message> WARNING: <xsl:value-of select="@href"/> has no test code. </xsl:message> </xsl:template> </xsl:stylesheet>
Here you see that the test code for a package simply loops over all
its xsl:include
elements and applies templates to them. This step causes each
included stylesheet tests to be exercised due to the aforementioned
xsl:include[@href=
'filename
']
part of the match.
Notice the template <xsl:template match="xsl:include" priority="-10">
. This template causes emission of a
warning if an included file does not contain test code. This concept
is important for quality control, since forgetting to create tests is
easy.
If you object to packaging the tests with
the actual code, you can achieve the
same effect by creating separate test files for each utility. In this
case, there is no need to use the id
attribute of
the stylesheet; simply match against the root:
<!-- math.max.test.xslt--> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://www.exslt.org/math" exclude-result-prefixes="math" xmlns:test="http://www.ora.com/XSLTCookbook/test"> <xsl:include href="../math/math.max.xslt"/> <!-- TEST CODE: DO NOT REMOVE! --> <xsl:template match="/ | xsl:include[@href='math.max.test.xslt']"> <xsl:message> TESTING math.max </xsl:message> <xsl:for-each select="document('')/*/test:test"> <xsl:variable name="ans"> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="test:data"/> </xsl:call-template> </xsl:variable> <xsl:if test="$ans != @ans"> <xsl:message> math:max TEST <xsl:value-of select="@num"/> FAILED [<xsl:value-of select="$ans"/>] </xsl:message> </xsl:if> </xsl:for-each> <!-- ... Same as math.max.xslt above ... --> </xsl:stylesheet>
You would then create separate test packages:
<!-- math.test.xslt --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math" extension-element-prefixes="math"> <xsl:include href="math.max.test.xslt"/> <xsl:include href="math.min.test.xslt"/> <!-- ... Same as math.xslt, above ... --> </xsl:stylesheet>
If you separate your tests in this way, be sure to ship the test code with the actual implementations. Doing so allows your clients to verify tests for themselves. The test code also doubles as an example of how to use the templates.