Notice how Recipe 13.4 embedded test data in the
test stylesheets. Each test element contains a test
num
attribute and the correct result in the form
of an ans
attribute. The test driver then extracts
these test elements from the stylesheet, executes the test, and
compares the expected answer against the actual answer. The most
important aspect of the test driver is that it produces no output
when the test succeeds.
Some of the best advice on automating testing is in Brian W. Kernighan’s and Rob Pike’s The Practice of Programming (Addison Wesley, 1999). The authors state that test programs should produce output only when tests fail. Why? Who wants to wade through pages of test output to look for cases where the test fail? If you expect test code to produce no output, you will quickly notice failures when there is output. Of course, you should test your test code to make sure it actually executes before relying on this testing technique.
The method that stores the answer as an attribute in the test element
works for simple tests that produce a primitive result. However, some
templates produce node sets. In this case, you might need to store
the correct answer as child elements in the tests. You can then use
the value set operations of Recipe 7.2 to compare
results. However, sometimes you can test node-set producing templates
more simply. Consider the test driver for the
math:lowest
template. Recall that
math:lowest
returns a node set consisting of all
instances of the lowest number in an input node set:
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns: math="http://www.exslt.org/math" exclude-result-prefixes="math test" xmlns: test="http://www.ora.com/XSLTCookbook/test" id="math:math.lowest"> <xsl:import href="math.min.xslt"/> <xsl:template name="math:lowest"> <xsl:param name="nodes" select="/.."/> <xsl:variable name="min"> <xsl:call-template name="math:min"> <xsl:with-param name="nodes" select="$nodes"/> </xsl:call-template> </xsl:variable> <xsl:choose> <xsl:when test="number($min) = $min"> <xsl:copy-of select="$nodes[. = $min]"/> </xsl:when> <xsl:otherwise/> </xsl:choose> </xsl:template> <!-- TEST CODE: DO NOT REMOVE! --> <xsl:template match="xsl:stylesheet[@id='math:math.lowest'] | xsl:include[@href='math.lowest.xslt'] " xmlns:exsl="http://exslt.org/common"> <xsl:message> TESTING math.lowest </xsl:message> <xsl:choose> <xsl:when test="function-available('exsl:node-set')"> <xsl:for-each select="document('')/*/test:test"> <xsl:variable name="ans"> <xsl:call-template name="math:lowest"> <xsl:with-param name="nodes" select="test:data"/> </xsl:call-template> </xsl:variable> <xsl:variable name="$ans-ns" select=" exsl:node-set($ans)"/> <xsl:if test="not($ans-ns/* != test:data[. = current( )/@ans]) and count($ans-ns/*) != count(test:data[. = current( )/@ans])"> <xsl:message> math:lowest TEST <xsl:value-of select="@num"/> FAILED [<xsl:copy-of select="$ans-ns"/>] [<xsl:copy-of select="test:data[. = current( )/@ans]"/>] </xsl:message> </xsl:if> </xsl:for-each> </xsl:when> <xsl:otherwise> <xsl:message> WARNING math.lowest test code requires exsl:node-set THIS VERSION=[<xsl:value-of select="system-property('xsl:version')"/>] VENDOR=[<xsl:value-of select="system-property('xsl:vendor')"/>] </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:template> <test:test num="1" ans="1" 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> <!-- more tests here ... > </xsl:stylesheet>
The comparison relies on the behavior of !=
when
both sides are node sets: the result is true
if a
pair of nodes, one from each node set, have different string values.
You can make sure that the nodes returned by selecting the answer
nodes from the test set are the same as the nodes returned by
math:lowest
. You can also make sure that the
counts are the same.
Some forms of computation (especially mathematical approximations) produce results that are correct even when the value produced is not exactly equal to the theoretically correct answer. In this case, you can include an error tolerance in the test data and make sure the computed answer is identical to the correct answer within the stated tolerance.