XML Unit: assertEquals and fail

Some weeks ago I was challenged at work that led me to invent a unit test framework for xml files. After some basic first steps I decided to introduce the approach on my blog as well.

Motivation

Why did I need a unit test framework for xml files? Easy enough. Our product has an intense configuration consisting of several xml files for the customization part. On the current project I’m working on there is a configuration file which consists of about 18 Megabytes of customization in one xml file. Most of our other tests use to start up a full system (taking a bit more than 60 Megs of main-memory database in combination with oracle database persistence) and exercise the whole chain all through the system. Initially I tried to write a unit test using JUnit and JDom, but failed to have it executed in my IDE while the 18 Megs were trying to be loaded with an OutOfHeapSpace exception. Brian Marick had pointed out Assert { xpath } from Ruby some weeks ago and I started to consider this as an alternative. After realizing that nearly no one knows Ruby at my company and there would be drawbacks considering the way our company uses the Revision Control system, I forgot about these alternative.

Then I reminded myself on an approach using xsltproc and some stylesheets. Usability for our Continuous Integration framework was an issue, therefore I decided to produce an output similar to the JUnit XML reports, so these can be directly hard-wired. This blog entry will describe the first few functions that I built: assertEquals and fail. If there is a demand for follow-ups, I will try to come up with an approach similar to the Test and the Suite classes of JUnit. In the end a test runner will also be needed, which is currently handled by Makefile dependencies.

First steps

I was considering to make a write-up similar to Kent Beck’s Test-driven development by example example on PyUnit. Since I did not develop this stylesheet in this manner, I would have the ability to train my practices. Since I consider myself unable to find the time needed for this, I decided to stick with showing the end results here for now, leaving some more work for the future.

The techniques that I used are part of the Extended XSLT syntax. For assertEquals and fail to work I need some function calls. This is achieved by functions in XSLT:

<func:function name="my:func">
  ...
  <func:result>
    ...
  </func:result>
</func:function>

Here is the code for the basic fail function, on which assertEquals is based:


  <func:function name="assert:fail">

    <xsl:param name="message" select="''" />
    
    <func:result>
      <xsl:element name="failure">
        <xsl:attribute name="message">
          <xsl:value-of select="$message" />
        </xsl:attribute>
      </xsl:element>
    </func:result>
  </func:function>

This function gets an optional parameter, the message. Like in JUnit itself, it produces a new subnode in the xml output structure like this:


  <failure message="message" />

The caller will have to take care of the surrounding xml node holding the actual test case. The message is defaulted to the empty text, so you leave it out and it will still produce an error.

How to use this? AssertEquals holds an example for this:


  <func:function name="assert:assertEquals">

    <xsl:param name="expected" />
    <xsl:param name="actual" />

    <xsl:param name="message" select="''" />

    <func:result>
      <xsl:if test="$actual != $expected">
        <xsl:variable name="failMessage">
          <xsl:value-of select="$message" />
          <xsl:text> Expected: </xsl:text>
          <xsl:value-of select="$expected" />
          <xsl:text> Actual: </xsl:text>
          <xsl:value-of select="$actual" />
        </xsl:variable>
        <xsl:copy-of select="assert:fail($failMessage)" />
      </xsl:if>
    </func:result>
  </func:function>

Just like one would assume, assertEquals gets at least two mandatory parameters: the expected value and the actual value. An optional message can be used just as in all other xUnit famliy test frameworks. expected and actual are checked for equality, and if this does not hold the error message is constructed. That’s it. Please note that since we’re dealing with xslt here, we can practically include any XPath entry when calling this function. I have to add a remark regarding the use: You have to use xsl:copy-of when calling this. xsl:value-of does not produce consistent results, i.e. leaving out the failure message when a test failed.

For convenience here is my assert.xslt, which is then included by other stylesheets, which use it. Feel free to copy and paste it for your need:


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:func="http://exslt.org/functions"
  xmlns:assert="http://some.url"
  extension-element-prefixes="func">

  <func:function name="assert:assertEquals">

    <xsl:param name="expected" />
    <xsl:param name="actual" />

    <xsl:param name="message" select="''" />

    <func:result>
      <xsl:if test="$actual != $expected">
        <xsl:variable name="failMessage">
          <xsl:value-of select="$message" />
          <xsl:text> Expected: </xsl:text>
          <xsl:value-of select="$expected" />
          <xsl:text> Actual: </xsl:text>
          <xsl:value-of select="$actual" />
        </xsl:variable>
        <xsl:copy-of select="assert:fail($failMessage)" />
      </xsl:if>
    </func:result>
  </func:function>

  <func:function name="assert:fail">

    <xsl:param name="message" select="''" />
    
    <func:result>
      <xsl:element name="failure">
        <xsl:attribute name="message">
          <xsl:value-of select="$message" />
        </xsl:attribute>
      </xsl:element>
    </func:result>
  </func:function>

</xsl:stylesheet>
  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Twitter
  • LinkedIn
  • Google Bookmarks

4 thoughts on “XML Unit: assertEquals and fail”

  1. XML Unit is not an option due to these two reasons:
    1. loading the 18 mb xml file did not work due to HeapSpace limitations.
    2. I needed to assert across several nodes, which are crossreferencing over the whole file.

    For convenience it worked better with the stylesheet approach. I’m planning to extend this in a future blog posting a little bit more.

Leave a Reply

Your email address will not be published. Required fields are marked *