Klaros-Testmanagement uses the JBoss Seam PDF framework to render the report and fill the retrieved data into the layout template.
We will start with an empty document that contains a header and footer definition.
<p:document xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://jboss.org/schema/seam/pdf" title="Klaros-Testmanagement Test Plan Report" marginMirroring="true" author="#{user.name}" creator="#{user.name}" pageSize="A4"> <f:facet name="header"> <p:font size="8"> <p:header borderWidthBottom="0.1" borderColorBottom="black" borderWidthTop="0" alignment="center"> <p:text value="Test Run Report - Generated on #{date} by #{user.name}" /> </p:header> <p:footer borderWidthTop="0.1" borderColorTop="black" borderWidthBottom="0" alignment="center"> <p:text value="Page " /> <p:pageNumber /> <p:text value=" - Created with Klaros-Testmanagement (www.klaros-testmanagement.com)" /> </p:footer> </p:font> </f:facet> <p:paragraph> <p:text value=" " /> </p:paragraph> </p:document>
This snippet creates a header that is displayed on every page.
It contains the date and the name of the Klaros-Testmanagement user who created the report.
The footer contains the page number and an informative text that the report was created with
Klaros-Testmanagement. Note that for the header the borderWidthBottom
attribute was set to provide a separation from the following text, while for the footer the
borderWidthTop
attribute was used to create a single line for separation.
Next we create a front page accumulating the main information about the report.
The layout of the front page is done using <p:paragraph>
elements for formatting.
These elements use alignment and spacing to define the position where the text will be placed.
To change the font of the paragraphs, you can use the <p:font>
element and use its style and size attributes to highlight certain parts.
<p:paragraph alignment="center" spacingAfter="100"> <p:text value="" /> </p:paragraph> <p:font style="bold" size="32"> <p:paragraph alignment="center" spacingAfter="75"> <p:text value="Test Run Report" /> </p:paragraph> </p:font> <p:font style="normal" size="12"> <p:paragraph alignment="center" spacingAfter="5"> <p:text value="Created by" /> </p:paragraph> </p:font> <p:font style="bold" size="16"> <p:paragraph alignment="center" spacingAfter="5"> <p:text value="#{user.name} (#{user.email})" /> </p:paragraph> </p:font> <p:font style="normal" size="12"> <p:paragraph alignment="center" spacingAfter="5"> <p:text value="on" /> </p:paragraph> </p:font> <p:font style="bold" size="16"> <p:paragraph alignment="center" spacingAfter="75"> <p:text value="#{date}" /> </p:paragraph> </p:font> <p:font style="normal" size="12"> <p:paragraph alignment="center" spacingAfter="30"> <p:text value="Testsuite " /> <p:text value="#{testSuite.name}" /> <p:text value=" - " /> <p:text value="#{testSuite.shortname}" /> <p:text value=" - revision " /> <p:text value="#{testSuite.revisionId}" /> </p:paragraph> <p:paragraph alignment="center" spacingAfter="5"> <p:text value="SUT: " /> <p:text value="#{testSuite.sut.name}" /> <p:text value=" - " /> <p:text value="#{testSuite.sut.productversion}" /> </p:paragraph> </p:font> <p:newPage />
An image of a sample front page can be found below.
As you might have noticed from the header and footer definition, values stored in the context can be accessed by preceding the context variable enclosed in curly brackets with a #, e.g. #{user.name}. The context variable for the user is automatically inserted into the context by Klaros-Testmanagement. You might remember the part where we prepared the data for this report and added the test suite to the context.
context.add("testSuite", testSuite);
For the front page we access this context variable to retrieve the
data that came with this test suite, e.g.
#{testSuite.name}, #{testSuite.shortname},
and
#{testSuite.revisionId}.
You can find the accessible attributes in the
Klaros-Testmanagement API Documentation
reference in the online documentation. All attributes that have a
getter can be accessed, e.g. for #{testSuite.name}
see the interface IKlarosTestSuite where you will find the method
getName in its IKlarosLabeledObject parent interface. To add a page break to the document, you can use
<p:newPage />
Now that we have a front page we can start to fill in the data we prepared and stored in the context. To give a quick overview over the test results we will start with a pie chart displaying the amount of successful, failed, skipped, and errorneous test case results in this test suite.
<p:paragraph horizontalAlignment="center" spacingAfter="25"> <p:piechart title="Testresults per Testrun" direction="anticlockwise" circular="true" startAngle="30" labelGap="0.1" labelLinkPaint="black" plotBackgroundPaint="white" labelBackgroundPaint="white" is3D="true" borderVisible="false"> <p:series key="results"> <p:data key="Error [#{error.size}]" value="#{error.size}" sectionPaint="#FF0A0A" /> <p:data key="Success [#{success.size}]" value="#{success.size}" sectionPaint="#33CC00" /> <p:data key="Failure [#{failure.size}]" value="#{failure.size}" sectionPaint="#FFCC00" explodedPercent=".2" /> <p:data key="Skipped [#{skipped.size}]" value="#{skipped.size}" sectionPaint="#FFFFFF" /> </p:series> </p:piechart> </p:paragraph>
An example of a generated pie chart can be found below.
For a detailed description about how to layout the pie chart and many
other charts, please check the
seam-pdf documentation.
Again you might remember that we added four List<KlarosTestCaseResult>
objects to
the context:
context.add("error", error); context.add("failure", failure); context.add("success", success); context.add("skipped", skipped);
These four objects represent the results of our test suite. We can simply use the size of the collections representing the data for the pie chart:
<p:data key="Error [#{error.size}]" value="#{error.size}" sectionPaint="#FF0A0A" />
Next we want to print a page for each Test Case contained in the test suite and present its data in a table.
As this might get a bit lengthy, only fragments will be presented here. An example can be found below.
First we need to loop over all test cases of the test suite:
<ui:repeat value="#{testSuite.testCases}" var="testCase"> ... </ui:repeat>
A loop can be done using the <ui:repeat> element.
The attribute value
gets a collection and the attribute
var
gets the name of a variable that can be used for further
processing, e.g. testCase
. With every step through the
loop, the variable testCase
will get the next element from the collection.
Now for the table. We need a two column table and the width of the second column should be three times the size of the first. The code is as follows:
<p:table columns="2" widths="1 3"> ... </p:table>
Let's get some data into the table. Therefore we have to describe
what each table cell will look like. This code goes inside the
<p:table>
element.
<p:cell horizontalAlignment="right"> <p:font style="bold" size="8"> <p:paragraph> <p:text value="Project: " /> </p:paragraph> </p:font> </p:cell> <p:cell> <p:paragraph alignment="left"> <p:text value="#{testCase.configuration.name}" /> </p:paragraph> </p:cell> <p:cell horizontalAlignment="right"> <p:font style="bold" size="8"> <p:paragraph> <p:text value="Creator: " /> </p:paragraph> </p:font> </p:cell> <p:cell> <p:paragraph alignment="left"> <p:text value="#{testCase.creator.name}" /> </p:paragraph> </p:cell> ...
Since we defined that the table should be two columns wide,
two <p:cell>
elements build one row in the table.
In the code above the first row holds the name of the project while the second line holds the
name of the user who created the test case. A cell can also contain text formatting information as
you can see from the <p:font>
elements.
Next we need the test case results for the test case being displayed. This is where another element comes in. If there is no test result for a test case contained in the test suite an informative text should be displayed instead of just leaving a blank space.
<ui:fragment rendered="#{testCase.results.isEmpty()}"> <p:font style="normal" size="14"> <p:paragraph alignment="left" spacingAfter="15" indentationLeft="10"> <p:text value="No test runs found for this Test Case." /> </p:paragraph> </p:font> </ui:fragment>
Notice the <ui:fragment rendered="...">
element. This part of the document is only integrated into the pdf if
the condition defined in the rendered attribute evaluates to true
.
This could be compared to an if-clause
in modern programming languages. In the expression
#{testCase.results.isEmpty()}
the isEmpty()
method of a the test case result list
is called which evaluates to a
boolean value. Next we have to define the block that displays
the data in case there are test results present for the test case:
<ui:fragment rendered="#{!testCase.results.isEmpty()}"> ... </ui:fragment>
Note the !
which is used to negate the boolean expression
we used in the block before. The code inside this expression is called if there is at least one test case
result.
And now we loop over the results of the test cases, which will include each test run for a test case. An example of a Test Run can be found below.
<ui:repeat value="#{testCase.results.toArray()}" var="testResult"> <!-- Start Test Result summary (equivalent to Test Run) --> ... </ui:repeat>
This will provide us with the test results of a test case in the
variable testResult
. The next pieces of code include the displaying of a
summary of the test result which is quite similar to the code used
displaying the test case summary. You can have a look at it in
the attached sourcefile. The next thing to display are the test case steps
and their results for the current test run.
Therefore another loop is added inside the Test Result loop.
<!-- Start Test Step Result summary --> <ui:fragment rendered="#{!testResult.stepResults.isEmpty()}"> <ui:repeat value="#{testResult.stepResults}" var="testStepResult"> ... </ui:repeat> </ui:fragment> <ui:fragment rendered="#{testResult.stepResults.isEmpty()}"> <p:font style="normal" size="10"> <p:paragraph alignment="left" spacingAfter="35" indentationLeft="25"> <p:text value="No test step results found." /> </p:paragraph> </p:font> </ui:fragment>
Inside the <ui:repeat> </ui:repeat>
block a table is
created. This table should be displayed if there are any results
for the test case step. The table displays the precondition, action, and
postcondition of the test case step and also the test case step result,
the test case step summary, and the test case step description. The test case step
result cell of the table will get coloured according
to the result, e.g. green if the step passed. Each table will have the number of the step as
a headline.
Displaying the step number.
<p:font style="bold" size="8"> <p:paragraph indentationLeft="20"> <p:text value="Step #{testResult.stepResults.indexOf(testStepResult)+1}" /> </p:paragraph> </p:font>
Here you can see how to retrieve the index of the test case step
result from the list of test results. testStepResult
is the variable from
the inner loop, while testResult
is the variable from the outer loop.
As counting starts at zero we have to increment the retrieved value,
otherwise the first step would be step 0.
Displaying the table if the test case has at least one test case step defined.
<p:table columns="3" widths="3 3 3" spacingBefore="5" rendered="#{testCase.testCaseSteps!=null and testCase.testCaseSteps.size() > 0}"> ... </p:table>
Retrieving the test case step properties (precondition, action, postcondition)
<p:cell horizontalAlignment="left"> <p:font style="normal" size="8"> <p:paragraph> <p:text value="#{testCase.testCaseSteps.get(testStepResult.precondition}" /> </p:paragraph> </p:font> </p:cell>
To get to the test case step properties we have to access the test
case to retrieve the data. From the list of test case steps we retrieve
the test case step via the index in the list of the test case step results and
then the precondition
can be accessed. The same applies to the
action
and the postcondition
of the test case
step.
Colouring a cell depending on the test step result.
<ui:fragment rendered="#{testStepResult.isPassed()}"> <p:cell backgroundColor="rgb(0,255,0)" horizontalAlignment="center"> <p:font style="normal" size="8"> <p:paragraph> <p:text value="Passed" /> </p:paragraph> </p:font> </p:cell> </ui:fragment> <ui:fragment rendered="#{testStepResult.isError()}"> <p:cell backgroundColor="rgb(255,0,0)" horizontalAlignment="center"> <p:font style="normal" size="8"> <p:paragraph> <p:text value="Error" /> </p:paragraph> </p:font> </p:cell> </ui:fragment> <ui:fragment rendered="#{testStepResult.isFailure()}"> <p:cell backgroundColor="rgb(255,215,0)" horizontalAlignment="center"> <p:font style="normal" size="8"> <p:paragraph> <p:text value="Failed" /> </p:paragraph> </p:font> </p:cell> </ui:fragment> <ui:fragment rendered="#{testStepResult.isSkipped()}"> <p:cell horizontalAlignment="center"> <p:font style="normal" size="8"> <p:paragraph> <p:text value="Skipped" /> </p:paragraph> </p:font> </p:cell> </ui:fragment>
The cells are rendered depending on the status of the test case step,
therefore the methods isError()
,
isFailure()
and so on are called for
the test result object under processing.
The cell gets its colour by setting the backgroundColor
attribute
to the desired rgb value.