XSLT and Schematron

Unless you plan on executing test scripts against a test system that only supports XML, it is highly recommended to write rules in Groovy as XSLT and Schematron rules can only be evaluated against requests and responses whose content is in XML while Groovy supports JSON as well.

Declarations

Below is the syntax for defining summary, description, and parameters in XSLT and Schematron. Please refer to Parameters for more information on how to supply parameters in test script.

Notice that the syntax is the same as that in Groovy. Only the comment characters are different. Instead of using /* and */, we’re using the XML comment characters <!-- and -->

<!--
   rule.summary=Response '${param.header}' header cannot be empty
   rule.description=Validates the '${param.header}' header in the response
   rule.param.header.required=true
-->

Header assertions

Suppose you wanted to check the ‘Content-Type’ header in the response and compare it to an expected value passed as a parameter from the test script.

Below is the same logic coded in three formats for comparison:

Groovy

/*
   rule.summary=Response Content-Type header must be ${expectedContentType}.
   rule.description=Validates the content type of the response
   rule.param.expectedContentType.required=true
*/
assert !param.expectedContentType.is(null): "The parameter 'expectedContentType' was not supplied"
assert !responseHeaders.header['Content-Type'].isEmpty(): "Could not find 'Content-Type' header"
assert  responseHeaders.header['Content-Type'].containsIgnoreCase(param.expectedContentType):
   "Expected Content-Type '"+ param.expectedContentType+"' did not match actual Content-Type '"
   + responseHeaders.header['Content-Type']+"'"

XSLT

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:saxon="http://saxon.sf.net/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:f="http://hl7.org/fhir" xmlns:t="http://touchstone.aegis.net/"
   version="2.0">
<!--
 rule.summary=Response Content-Type header must be ${expectedContentType}.
 rule.description=Validates the content type of the response
   rule.param.expectedContentType.required=true
-->
   <xsl:output method="xml" omit-xml-declaration="no" standalone="yes" indent="yes" />
   <xsl:param name="param" />
   <xsl:param name="headers" />
   <xsl:template match="/">
      <t:validationresult>
         <xsl:choose>
            <xsl:when test="$param">
               <xsl:choose>
                  <xsl:when test="$headers"> <!-- You can also use 'responseHeaders' or 'requestHeaders'.
                                 The right one will be supplied in place of headers depending on direction -->
                     <xsl:choose>
                        <xsl:when test="$param/expectedContentType and $param/expectedContentType/text()">
                           <xsl:choose>
                              <xsl:when test="$headers/Content-Type">
                                 <xsl:choose>
                                    <xsl:when test="contains(lower-case($headers/Content-Type/text()),
                                       lower-case($param/expectedContentType))" />
                                    <xsl:otherwise>
                                       <t:error>Expected Content-Type '<xsl:value-of
                                          select="$param/expectedContentType" />'
                                          did not match actual Content-Type '<xsl:value-of
                                          select="$headers/Content-Type" />'.</t:error>
                                    </xsl:otherwise>
                                 </xsl:choose>
                              </xsl:when>
                              <xsl:otherwise>
                                 <t:error>Could not find 'Content-Type' header.</t:error>
                              </xsl:otherwise>
                           </xsl:choose>
                        </xsl:when>
                        <xsl:otherwise>
                           <t:error>The parameter 'expectedContentType' was not supplied.</t:error>
                        </xsl:otherwise>
                     </xsl:choose>
                  </xsl:when>
                  <xsl:otherwise>
                     <t:error>The headers were not supplied.</t:error>
                  </xsl:otherwise>
               </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
               <t:error>The parameters were not supplied.</t:error>
            </xsl:otherwise>
         </xsl:choose>
      </t:validationresult>
   </xsl:template>
</xsl:stylesheet>

Schematron

<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <sch:ns prefix="f" uri="http://hl7.org/fhir"/>
  <sch:ns prefix="h" uri="http://www.w3.org/1999/xhtml"/>

<!--
 rule.summary=Response Content-Type header must be ${expectedContentType}.
 rule.description=Validates the content type of the response
   rule.param.expectedContentType.required=true
-->
    <xsl:param name="param" />
    <xsl:param name="headers" />   <!-- You can also use 'responseHeaders' or 'requestHeaders'.
                     The right one will be supplied in place of headers depending on direction -->
    <sch:pattern>
    <sch:title>RuleContentType</sch:title>
    <sch:rule context="/">
      <sch:assert test="$param">The parameters were not supplied.</sch:assert>
      <sch:assert test="$headers">The headers were not supplied.</sch:assert>
      <sch:assert test="$param/expectedContentType">The parameter 'expectedContentType' was not supplied.
                                                      </sch:assert>
      <sch:assert test="$param/expectedContentType/text()">The parameter 'expectedContentType' was not
                                          supplied.</sch:assert>
      <sch:assert test="$headers/Content-Type">Could not find 'Content-Type' header.</sch:assert>
      <sch:assert test="contains(lower-case($headers/Content-Type/text()), lower-case($param/expectedContentType))">
             Expected Content-Type '<xsl:value-of select="$param/expectedContentType"/>' did not match actual
             Content-Type '<xsl:value-of select="$headers/Content-Type"/>'.</sch:assert>
    </sch:rule>
  </sch:pattern>
</sch:schema>

Payload assertions

Suppose you wanted to determine the FHIR resource within the response and compare it to an expected value passed as a parameter from the test script.

Below is the same logic coded in three formats for comparison:

Groovy

/*
 rule.summary=Response resource must be ${expectedResource}.
 rule.description=Validates the type of the resource in the response.
 rule.param.expectedResource.required=true
 */
assert response.resource==${param.expectedResource}: "Expected resource '"
   +param.expectedResource +"' did not match actual resource '"+response.resource+"'"

XSLT

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:saxon="http://saxon.sf.net/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:f="http://hl7.org/fhir"
      xmlns:t="http://touchstone.aegis.net/" version="2.0">

<!--
 rule.summary=Response resource must be ${expectedResource}.
 rule.description=Validates the type of the resource in the response.
 rule.param.expectedResource.required=true
-->
   <xsl:output method="xml" omit-xml-declaration="no" standalone="yes" indent="yes" />
   <xsl:param name="param" />
      <xsl:template match="/">
      <t:validationresult>
         <xsl:choose>
            <xsl:when test="$param">
               <xsl:choose>
                  <xsl:when test="$param/expectedResource and $param/expectedResource/text()">
                     <xsl:choose>
                        <xsl:when test="*[1]/name()=$param/expectedResource" />
                        <xsl:otherwise>
                           <t:error>Expected resource '<xsl:value-of select="$param/expectedResource"/>'
                               did not match actual resource '<xsl:value-of select="*[1]/name()"/>'.</t:error>
                        </xsl:otherwise>
                     </xsl:choose>
                  </xsl:when>
                  <xsl:otherwise>
                     <t:error>The parameter 'expectedResource' was not supplied.</t:error>
                  </xsl:otherwise>
               </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
               <t:error>The parameters were not supplied.</t:error>
            </xsl:otherwise>
         </xsl:choose>
      </t:validationresult>
   </xsl:template>
</xsl:stylesheet>

Schematron

<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <sch:ns prefix="f" uri="http://hl7.org/fhir"/>
  <sch:ns prefix="h" uri="http://www.w3.org/1999/xhtml"/>

<!--
 rule.summary=Response resource must be ${expectedResource}.
 rule.description=Validates the type of the resource in the response.
 rule.param.expectedResource.required=true
-->

   <xsl:param name="param" />
    <sch:pattern>
    <sch:title>RuleResource</sch:title>
    <sch:rule context="/">
      <sch:assert test="$param">The parameters were not supplied.</sch:assert>
      <sch:assert test="$param/expectedResource">The parameter 'expectedBundleResource'
               was not supplied.</sch:assert>
      <sch:assert test="*[1]/name()=$param/expectedResource">Expected resource
         '<xsl:value-of select="$param/expectedResource"/>' did not match actual resource
         '<xsl:value-of select="*[1]/name()"/>'.</sch:assert>
    </sch:rule>
  </sch:pattern>
</sch:schema>