Eingebettete Unit-Tests zu Hilfs-Stylesheets hinzufügen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie möchten Tests zu Ihren Hilfs-Stylesheets hinzufügen, damit diese jederzeit überprüft werden können.

Lösung

Das folgende Stylesheet ist dazu gedacht, als Hilfsmittel hinzugefügt zu werden. Allerdings bietet dieses Beispiel die Möglichkeit, das Stylesheet zu testen, indem es als sein eigenes Eingabedokument ausgeführt wird:

<!-- 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 &gt; $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: NICHT ENTFERNEN! -->
  <xsl:template match="/xsl:stylesheet[@id='math:math.max'] | xsl:include[@href='math.max.xslt'] " priority="-1000">
    <xsl:message>TESTEN von 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 von <xsl:value-of select="@num"/> FEHLGESCHLAGEN [<xsl:value-of select="$ans"/>]</xsl:message>
     </xsl:if>
   </xsl:for-each>
   <!-- Test auf unendlich -->
   <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 TEST auf unendlich FEHLGESCHLAGEN [<xsl:value-of select="$ans1"/>]</xsl:message>
   </xsl:if>
   <!-- Test auf -unendlich -->
   <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 TEST auf -unendlich FEHLGESCHLAGEN [<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>

Diskussion

Das Element xsl:stylesheet verfügt über ein optionales Attribut namens id. Dieses Attribut idenfiziert Stylesheets, die in umfangreichen Dokumenten eingebettet sind. Hier jedoch wird die ID zu Testzwecken benutzt. Was Sie möchten, ist Testcode derart im Stylesheet unterzubringen, dass Sie davon ausgehen können, dass er die normale Verwendung des Stylesheets nicht behindert. Das erreichen Sie, indem Sie ein Template erzeugen, das nur dann passt, wenn das Stylesheet sich selbst verarbeitet:

<xsl:template match="/xsl:stylesheet[@id='math:math.max'] | xsl:include[@href='math.max.xslt']">

Das erklärt zunächst das /xsl:stylesheet[@id='math:math.max'], aber was macht der Teil xsl:include[@href='math.max.xslt']? Damit Sie seinen Wert besser erkennen können, folgt nun ein Stylesheet, das all Ihre mathematischen Hilfskonstrukte in eine einzige Datei packt, die sich leicht importieren lässt. Dabei hätten Sie auch gern eine einfache Möglichkeit, um das gesamte Paket zu testen:

<!-- 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>TESTEN von 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>WARNUNG: <xsl:value-of select="@href"/> enthält keinen Testcode.</xsl:message>
  </xsl:template>
</xsl:stylesheet>

Hierbei sehen Sie, wie der Testcode eines Pakets in einer Schleife über all seine xsl:include-Elemente geht und Templates darauf anwendet. Bei diesem Schritt wird wegen des vorher erwähnten Teils xsl:include[@href='filename'] in der Übereinstimmung der jeweilige Stylesheet-Test ausgeführt.

Beachten Sie besonders das Template <xsl:template match="xsl:include" priority="-10">. Dieses Template gibt eine Warnung aus, falls eine importierte Datei keinen Testcode enthält. Die Idee dahinter spielt eine wichtige Rolle bei der Qualitätssicherung, weil man leicht vergisst, Tests zu erstellen.

Wenn Sie den Testcode lieber nicht mit dem eigentlichen Code vermengen möchten, dann können Sie den gleichen Effekt dadurch erreichen, dass Sie für jedes Hilfskonstrukt eine separate Testdatei erstellen. In diesem Fall müssen Sie nicht das id-Attribut des Stylesheets verwenden, sondern können einfach eine Übereinstimmung mit der Wurzel vornehmen:

<!-- 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: NICHT ENTFERNEN! -->
  <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 von <xsl:value-of select="@num"/> FEHLGESCHLAGEN [<xsl:value-of select="$ans"/>]</xsl:message>
      </xsl:if>
    </xsl:for-each>
    <!-- ... Identisch mit math.max.xslt oben ... -->
</xsl:stylesheet>

Dann würden Sie separate Testpakete erstellen:

<!-- 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"/>
  <!-- ... Identisch mit math.xslt, oben ... -->
</xsl:stylesheet>

Wenn Sie Ihre Tests auf diese Weise vom eigentlichen Code trennen, sollten Sie aber dafür sorgen, dass Sie den Testcode mit der wirklichen Implementierung auch ausliefern. Das gibt Ihren Kunden die Möglichkeit, die Tests selbst zu überprüfen. Der Testcode hat außerdem den zusätzlichen Nutzen, dass er als Beispiel dafür dient, wie man die Templates benutzen kann.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de