So ist ein XSPec-Test aufgebaut
Eine XSpec-Datei ist selbst XML. Wer mit XSLT arbeitet, findet sich deshalb schnell zurecht. Die wichtigsten Elemente sind x:description für das getestete Modul, x:scenario für einen Testfall und x:expect für das erwartete Ergebnis.
Mit x:call rufen wir eine Funktion oder ein benanntes Template auf. Mit x:context geben wir XML-Knoten vor, auf die passende Template-Regeln angewendet werden. Der XSpec-Namensraum lautet
http://www.jenitennison.com/xslt/xspec
Funktionen und Templates aufrufen
Das folgende Beispiel erzeugt eine Literaturangabe. Ein Test ruft das benannte Template direkt auf. Ein zweiter gibt ein book-Element vor und lässt es im Modus citation verarbeiten:
examples/03-templates/citation.xspec
<?xml version="1.0" encoding="UTF-8"?>
<x:description xmlns:x="http://www.jenitennison.com/xslt/xspec"
stylesheet="citation.xsl">
<x:scenario label="Call the citation template by name">
<x:call template="format-citation">
<x:param name="title" select="'Practical XML'"/>
<x:param name="year" select="2026"/>
</x:call>
<x:expect label="Create a citation"><cite>Practical XML (2026)</cite></x:expect>
</x:scenario>
<x:scenario label="Select the citation mode">
<x:context mode="citation" select="book">
<book year="2026"><title>Practical XML</title></book>
</x:context>
<x:expect label="Use the matching template"><cite>Practical XML (2026)</cite></x:expect>
</x:scenario>
</x:description>
Dazu gehört dieses Stylesheet:
examples/03-templates/citation.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">
<xsl:template name="format-citation">
<xsl:param name="title" as="xs:string"/>
<xsl:param name="year" as="xs:integer"/>
<cite><xsl:value-of select="concat($title, ' (', $year, ')')"/></cite>
</xsl:template>
<xsl:template match="book" mode="citation">
<xsl:call-template name="format-citation">
<xsl:with-param name="title" select="string(title)"/>
<xsl:with-param name="year" select="xs:integer(@year)"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Die beiden Tests sehen ähnlich aus, prüfen aber nicht dasselbe. Der erste prüft das benannte Template. Der zweite zeigt zusätzlich, ob die passende Template-Regel ausgewählt wird und die Daten richtig weitergibt.
Bei benannten Templates werden Parameter über name zugeordnet. Bei Funktionen entscheidet ihre Reihenfolge. Bei Kontexttests sollte der Modus stimmen. Sonst wird möglicherweise nur eine eingebaute Standardregel ausgeführt, obwohl eigentlich eine eigene Regel getestet werden sollte.
Eine Zahl ist nicht dasselbe wie Text
Diese Schreibweisen sehen fast gleich aus, liefern aber unterschiedliche Werte:
<x:param select="27"/>
<x:param select="'27'"/>
<x:param>27</x:param>
select="27" liefert eine Zahl. select="'27'" liefert einen String. Inhalt zwischen den Start- und End-Tags erzeugt dagegen XML-Inhalt mit einem Textknoten. Eine Funktion kann solche Werte teilweise umwandeln, aber darauf sollte der Test nicht zufällig angewiesen sein. Für Zahlen und Strings verwenden die Beispiele deshalb select.
Größere Testdaten lassen sich aus Dateien laden. Mit href wird die Datei angegeben, mit select der gewünschte Ausschnitt gewählt:
<x:param href="catalog.xml" select="/catalog"/>
<x:expect href="published.xml" select="/catalog/book"/>
Hier bekommt die Funktion das catalog-Element. Als Ergebnis werden die book-Elemente aus einer anderen Datei erwartet. Die Pfade sind relativ zur jeweiligen Testdatei. Beim Kopieren eines Beispiels muss deshalb auch die Ordnerstruktur erhalten bleiben.
Ergebnisse vergleichen oder Eigenschaften prüfen
Manchmal steht das Ergebnis genau fest: Es soll AA herauskommen oder ein bestimmtes XML-Element entstehen. Manchmal interessiert eine Eigenschaft: Ist der String nicht leer? Enthält er nur Großbuchstaben? Dafür gibt es XPath-Ausdrücke in test.
Das folgende gemeinsame Szenario prüft einen nichtleeren String in Großschreibung:
examples/08-organization/shared.xspec
<x:scenario xmlns:x="http://www.jenitennison.com/xslt/xspec"
xmlns:xs="http://www.w3.org/2001/XMLSchema" label="A non-empty uppercase string" shared="yes">
<x:expect label="Return a non-empty string" result-type="xs:string"
test="string-length($x:result) gt 0"/>
<x:expect label="Use uppercase characters" result-type="xs:string"
test="$x:result eq upper-case($x:result)"/>
</x:scenario>
Mit result-type wird zuerst geprüft, ob der Rückgabewert den erwarteten Typ hat. Das hilft bei der Fehlersuche: Liefert die Funktion statt eines Strings plötzlich etwas anderes, soll der Test das verständlich melden.
Beim Vergleich von XML geht es um die Baumstruktur, nicht um die Bytes einer gespeicherten Datei. Eine andere Attributreihenfolge oder Einrückung ist ein anderes Thema als ein fehlendes Element. Leerzeichen im Text können dagegen sehr wohl wichtig sein. Für solche Fälle sind separate Eingabe- und Erwartungsdateien praktisch: Dann verändert das Einrücken der XSpec-Datei nicht versehentlich die Testdaten.





