Login or register for a free account

Custom Dashboards with XSLT

Ever wished you could use the information from the dashboard to generate your own reports?

Need a Custom Dashboard?

JUnit Factory users often ask if there is a way to extract the raw data from the Developer Dashboard and use it in their own reports. Some would like to combine the data with information from another system; others want to present a different view on the data or to change the look and feel to match their local style guidelines. This article will show you how you can use XSLT to do all of those things and more.

Every time you request a Developer Dashboard, JUnit Factory also generates a dashboard.xml file that contains all of the data you need to produce your own dashboard reports. I'll demonstrate how you can use XSLT to transform the dashboard.xml file to produce a custom report.

Generating a Dashboard XML File

If you haven't already generated a dashboard for your project, do it now - the instructions are in the sidebar over on the right. Once you have your dashboard, open it in a browser.

Here's mine:

Developer Dashboard

Now, in your browser's address bar, replace the last part of the url ('index.html') with 'dashboard.xml' and you should see something like this:

Developer Dashboard

As you can see, the dashboard.xml file contains a lot of data. To make it more useful, I am going to extract a subset of that data and present it more clearly. But, before I get that, I am going to tell you a little about XSLT.

About XSLT (and XPath)

XSL stands for eXtensible Stylesheet Language and XSLT stands for XSL Transformations. In short, XSLT is an XML-based language for transforming one XML document into another. In this example, I will transform the dashboard.xml into XHTML.

This is not an XSLT tutorial and I won't try to teach you everything there is to know about XSLT - just enough to copy/paste into your own stylesheet without hurting yourself. If you want to learn more, there is a rather good tutorial over at www.w3schools.com.

Templates

The easiest way to think about XSLT is as a collection of templates. Each template has an associated rule. When you apply an XSLT stylesheet to an input document, the XSLT processor walks through the nodes of the input document and, for each node, looks for a template with a rule that matches. The processor then uses each matching template to generate an output document.

For example, you might have a document that looks like this...


<catalog>
  <artist name='Bob Dylan'>
        <cd title='Blonde on Blonde' />
        <cd title='Slow Train Coming' />
        <cd title='Pat Garrett and Billy the Kid' />
  </artist>
  <artist name='Dire Straits'>
        <cd title='Making Movies' />
        <cd title='Love Over Gold' />
  </artist>
.
.
.
</catalog>

...and a template like this...


<xsl:template match='/catalog/artist/cd'>
    <h4><xsl:value-of select="@title"/></h4>
</xsl:template>
      

Now, imagine the XSLT processor walking down through all the nodes of the catalog document. Each time it reaches a cd element, it will find the matching template and apply it.

The template rule matches each cd element and emits the value of the title attribute inside an h4 tag and the resulting output will look like this:


   <h4>Slow Train Coming</h4>
   <h4>Pat Garrett and Billy the Kid</h4>
   <h4>Making Movies</h4>
   <h4>Love Over Gold</h4>
      

XPath

The template rules are written in an expression language called XPath. XPath expressions can get quite complicated but, for most purposes, you can think of XPath as like a filesystem path where the components of the path represent elements in an xml tree instead of files and folders.

Like filesystem paths, XPath has the concept of an absolute path (which starts with a slash) and a relative path (which doesn't).

There are additional XPath expressions using '@' to match xml attributes - like /catalog/artist/@name - and expressions to match particular elements - like /catalog/artist[2] - but I'll introduce those as I use them.

W3Schools has an XPath tutorial too if you want to learn more.

XML Namespaces

Whenever you mix two different xml formats in the same document, you need to tell the xml parser which element belongs to which format (XML without namespaces would be just ML). In my example, I am mixing XHTML and XSL so I have to prefix all the XSL elements with a namespace prefix like this xsl:.

Applying an XSLT Stylesheet

There are many ways to apply an XSLT stylesheet to a document. Here is a list of some of the easiest ways:

  1. You can use the Ant task:
  2. 
    <xslt in='dashboard.xml'
             out='custom-dashboard.html'
             style='custom-dashboard.xslt'/>
          
  3. You can use JavaScript to transform the document inside the browser (W3Schools has a tutorial).
    var xslt  = new XSLTProcessor();
    xslt.importStylesheet(stylesheet);
    result = xslt.transformToDocument(xml);
            
  4. You can add a reference to your stylesheet in the xml document:
    
    <?xml-stylesheet type="text/xsl" href="custom-dashboard.xslt"?>
          

If I were doing this in a production environment, I would probably use the Javascript approach but, with each new technology that I introduce, I decrease the odds that you can successfully follow along. In this tutorial, for the sake of simplicity, I will use the first approach. I will download the dashboard.xml file from www.junitfactory.com and then edit the file, adding a reference to my stylesheet.

What Do I Want My Custom Dashboard to Show?

I have often found it useful, after generating tests with JUnit Factory, to scan through the generated test methods to see which exceptions get thrown by my code. Some of those exceptions will be expected but some might highlight an input (like and empty string or a negative number) that I was not expecting. In cases like this I might need to revisit the code to make it more bullet-proof.

The Outline View provides a good way to review the test cases one at a time... Outline View ...but wouldn't it be nice to see all of the exceptions thrown by all of my methods in a single report?

That's what I want my custom dashboard to show and I'll build up the XSLT to do that one small step at a time.

One Small Step at a Time

I'll start by creating a very simple stylesheet, called custom-dashboard.xslt, to make sure that this approach works with my browser (I am using Firefox but it should also work with Internet Explorer and Safari):


<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">

    <html>
        <body>
          <h1>Hello, World</h1>
        </body>
        </html>
  </xsl:template>

</xsl:stylesheet>
      

Make sure there is no whitespace before the xml declaration or the parser will complain.

This stylesheet contains one template that will match the root (match="/") element and emit an output document that says Hello, World.

Now I'll edit my dashboard.xml file to reference that stylesheet...


  <?xml version="1.0" ?>
  <?xml-stylesheet type="text/xsl" href="custom-dashboard.xslt"?>
  <agitar-dashboard projectName="genetic" currentDate="12/6/07 12:08 PM">
      

...and, when I open the xml file in a modern browser, I see:

The First Transformation

The transformation worked but it does not use the input document at all. To make it more interesting, I'll extract the project name from the dashboard.xml file and show it in the heading.

Let's take a look at my dashboard.xml file. The root element of my xml file looks like this:


<agitar-dashboard projectName="genetic" currentDate="12/6/07 12:08 PM">
      

I can retrieve the projectName from the agitar-dashboard element using xsl:value-of with the appropriate XPath expression in the select attribute.

Here's the template so far with my latest changes in bold...


  <xsl:template match="/">
    <html>
        <body>
          <h1>Exception Report</h1>
          <h2>Project - <xsl:value-of select="/agitar-dashboard/@projectName"/></h2>
        </body>
        </html>
  </xsl:template>
      

...which gives me:

Added the Project Name

I'll add the test methods next.

Looking for Test Methods

Even though dashboard.xml doesn't include a DTD or a schema, it's easy to scan though the file and figure out what it contains. The data that I need is in the testmethod element.


<testclass name="com.diamondsky.creatures.CreatureAgitarTest" methodCount="53" owners="Not Owned" failureCount="0">
  <testmethod name="testBite()" status="Passed"/>
  <testmethod name="testConstructor()" status="Passed"/>
  <testmethod name="testConstructor1()" status="Passed"/>
  <testmethod name="testEat()" status="Passed"/>
  <testmethod name="testEatThrowsClassCastException()" status="Passed"/>
  <testmethod name="testEatThrowsNullPointerException()" status="Passed"/>
  ...
</testclass>
      

I can now write a new template to select all the testmethods and print their names...


  <xsl:template match="testmethod">
          <xsl:value-of select="@name"/><br />
  </xsl:template>
      

...but, to make sure that template gets applied, I have to modify my existing template to tell it to continue processing. I call to apply-templates tells the XSLT processor to process all of the descendents of the current node...


  <xsl:template match="/">
    <html>
        <body>
          <h1>Exception Report</h1>
          <h2>Project - <xsl:value-of select="/agitar-dashboard/@projectName"/></h2>
          <xsl:apply-templates/>
        </body>
        </html>
  </xsl:template>
      

...and when I refresh my browser, I see:

All the test methods

That shows all the tests methods, but I only want the methods with a name attribute that contains the substring 'Throws' so I modify the XPath rule to use a predicate and the contains() function. A predicate expression is indicated by square brackets and is evaluated on the current node.


  <xsl:template match="testmethod[contains(@name, 'Throws')]">
          <xsl:value-of select="@name"/><br />
  </xsl:template>
      
Exception methods only

The report would be easier to follow if it included class names so I'll add a new template. I'll also modify the existing one to show the test methods for each class in a list...


  <xsl:template match="testclass">
    <xsl:value-of select="@name"/>
    <ul>
    <xsl:apply-templates/>
    </ul>
  </xsl:template>

  <xsl:template match="testmethod[contains(@name, 'Throws')]">
    <li><xsl:value-of select="@name"/></li>
  </xsl:template>
      

It's a bit confusing to show the classes with no test methods, so I'll modify the rule to exclude them:


  <xsl:template match="testclass[testmethod[contains(@name, 'Throws')]]">
    <xsl:value-of select="@name"/>
    <ul>
    <xsl:apply-templates/>
    </ul>
  </xsl:template>
      

Here's how the report looks without the empty classes...

Report with no style

...and here's the full stylesheet so far:

      
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <html>
      <body>
        <h1>Exception Report</h1>
        <h2>Project - <xsl:value-of select="/agitar-dashboard/@projectName"/></h2>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>


  <xsl:template match="testclass[testmethod[contains(@name, 'Throws')]]">
    <xsl:value-of select="@name"/>
    <ul>
      <xsl:apply-templates/>
    </ul>
  </xsl:template>

  <xsl:template match="testmethod[contains(@name, 'Throws')]">
    <li><xsl:value-of select="@name"/></li>
  </xsl:template>

</xsl:stylesheet>
      
    

Looking Good

My custom dashboard now has all the information I need to analyze the unexpected exceptions but, before I finish, I'll use a little CSS magic to make it match the rest of the JUnit Factory site.

Here's the final stylesheet with CSS and here's how it looks when I apply it to my dashboard.xml file:

Final Report

More Reports

In this article, I showed how to generate a custom report using three very simple templates. Next time, I'll use some more advanced XSLT features to group and summarize the data and build a more sophisticated report.

The dashboard.xml is a new feature and we are still looking for feedback on how to improve it so let us know if you run into any problems. Please also tell us about any other custom reports that you generate from the dashboard and, if there are enough, I'll put together a showcase of some of the best.