Now that I’ve had the opportunity to work on a continuous integration infrastructure for both Mirth Connect interfaces and a MarkLogic-powered REST API. The Mirth interface testing software was built with Perl and TAP (Test Anything Protocol). The ML REST API testing software was built on MarkLogic itself. What they both had in common is that Jenkins was used to build the environments and run the tests.
I’ve really come to appreciate how useful Jenkins can be. Jenkins can aggregate jUnit style reports out of the box, and Jenkins can give you meaningful textual and graphical representations of those reports. It will tell you how many tests failed, how many tests passed, what class and packages were being tested, and it give you this information over the course of many builds.
However, since neither CI testing software was actually built on Java, I was unable to leverage the actual jUnit testing framework. In addition, neither the Mirth Connect interfaces nor MarkLogic/Xquery have a concept of grouping code into packages and classes as a Java programmer may expect. Because of these two issues, I had to generate jUnit output programmatically. Even though I was able to start with someones stab at the jUnit XML schema, I found that Jenkins does not parse and display the jUnit output exactly as I would have expected. For the time being, it is probably easier to ignore the jUnit schema and instead listen to what I’m about to say ?
Before I go into detail, I want to be forthright about a couple of things. Of everything I have learned and am about to write about, I have learned through my own experimentation. I am writing about it because I could find no good source detailing how Jenkins parses jUnit output. If there is a help page or doc that I have missed, please let me know! Because of that, it is very possible and likely that Jenkins can use even more information from a jUnit report that I haven’t discovered, so don’t take this as Jenkins gospel. Without further ado, let’s look at an example output file that I dreamed up. It may not be real-world accurate, but it will demonstrate what I want to show.
<testsuites> <testsuite name="Customer Demographics Tests" tests="9" failures="0" timestamp="2012-08-23T14:40:44.874443-05:00"> <testcase name="Insert First Name" classname="CustomerDemographicsPKG.Name"/> <testcase name="Insert Last Name" classname="CustomerDemographicsPKG.Name"/> <testcase name="Change First Name" classname="CustomerDemographicsPKG.Name"/> <testcase name="Change Last Name" classname="CustomerDemographicsPKG.Name"/> <testcase name="Delete First Name" classname="CustomerDemographicsPKG.Name"/> <testcase name="Delete Last Name" classname="CustomerDemographicsPKG.Name"/> <testcase name="Insert DOB" classname="CustomerDemographicsPKG.Misc"/> <testcase name="Update DOB" classname="CustomerDemographicsPKG.Misc"/> <testcase name="Delete DOB" classname="CustomerDemographicsPKG.Misc"/> </testsuite> <testsuite name="Customer Contact Tests" tests="4" failures="1" timestamp="2012-08-23T14:40:44.874443-05:00"> <testcase name="Insert Street" classname="CustomerContact.Address"/> <testcase name="Insert City" classname="CustomerContact.Address"/> <testcase name="Insert State" classname="CustomerContact.Address"/> <testcase name="Zip" classname="CustomerContact.Address"> <error message="Zip code out of range">Zip code 139819 is not in range of valid zip codes.</error> </testcase> <testcase name="Insert Home Phone" classname="CustomerContact.Phone"/> <testcase name="Insert Cell Phone" classname="CustomerContact.Phone"/> <testcase name="Insert Business Phone" classname="CustomerContact.Phone"/> </testsuite> </testsuites>
Main project status page
Even though the main project status page doesn’t directly render anything from the jUnit report, I want to show it anyway. In particular, the project status page gives us a list of build history in the left pane and some permalinks to important build events such as last stable, last failed, last unsuccessful build, and a few others. One of my favorite features is the graph which plots number of tests performed and number of failures over an arbitrary number of builds. Very cool.
As you can see, there is a link for “Latest Test Result”, and that link indicates that there was 1 failure. Since none of the generated report is used on this page, go ahead and click the “Latest Test Result” link and we’ll look at…
Package level test results
Finally, the good stuff! As an aside, I want to point out that this page was my inspiration for writing this post. First, in my most recent work, Xquery does have any understanding of packages; it is not OOP, it is functional, and does not support grouping modules as “packages.” Secondly, the “package” attribute on the “TestSuite” element is not parsed by Jenkins at all! It took a minute or six of Googling to narrow down how to get Jenkins to separate my tests into packages instead of grouping them all as “root.”
First, we’ll see a list of all the individual tests that failed, regardless of which package that test belonged to. Then, we’ll see a list of packages that were tested for this build.
For the “All Failed Tests” list, you can get a preview of what we’ll be discussing shortly. Jenkins identifies the failed tests as {package}.{class}.{test}. Similarly, the packages listed in the “All Tests” list are parsed from the jUnit report based the “classname” attribute on the “testcase” elements where the left hand of the period is the package name and the right hand of the period is the class name. I think that is misleading. In any case, by examining this snippet and the above image, I hope it is easy to visualize what I just explained :).
<!-- All the information on this page is based on the testcase element --> <testcase name="Insert First Name" classname="CustomerDemographicsPKG.Name"/>
Next, we’ll dive a page deeper by clicking a package name. Since CustomerContact package has a failure, I will examine that one. That link leads us to…
Class level test results
This page is very similar to the previous page, except instead of listing tests at the package level, they’re listed at the class level. We still do get a list of all the tests that failed.
I think this page is even easier to see how the the XML is visualized. Notice that there are a total of 7 tests, 4 from the Address class and 3 from the Phone class, and as mentioned earlier, Address and Phone are on the right hand side of the period in the “classname” attribute of the “testcase” elements.
<testcase name="Insert Street" classname="CustomerContact.Address"/> <testcase name="Insert City" classname="CustomerContact.Address"/> <testcase name="Insert State" classname="CustomerContact.Address"/> <testcase name="Zip" classname="CustomerContact.Address"> <error message="Zip code out of range">Zip code 139819 is not in range of valid zip codes.</error> </testcase> <testcase name="Insert Home Phone" classname="CustomerContact.Phone"/> <testcase name="Insert Cell Phone" classname="CustomerContact.Phone"/> <testcase name="Insert Business Phone" classname="CustomerContact.Phone"/>
Now is a good time to notice that, under the “All Failed Tests”, the test name really, truly is parsed from the “name” attribute on the “testcase” elements. The “classname” attribute compounds {package}.{class} and “name” visualizes the name of the test. We’ll see that more obviously on the next page, after I click “Address” in the list of classes tested.
The results of the actual tests!
This page is less exciting, but it arguably the most important. Here, we get a clean, simple table of the tests that were executed and whether they passed or failed. Also, depending on the same test on the previous build, the test’s status may also be regression or fixed.
It’s easy to see how the test names are generated from the jUnit reports. They are simply the “name” attribute on the “testcase” elements.
<testcase name="Insert Street" classname="CustomerContact.Address"/> <testcase name="Insert City" classname="CustomerContact.Address"/> <testcase name="Insert State" classname="CustomerContact.Address"/> <testcase name="Zip" classname="CustomerContact.Address"> <error message="Zip code out of range">Zip code 139819 is not in range of valid zip codes.</error> </testcase>
Since there were three tests that passed, and those tests aren’t going to give any interesting output, we’re going to look at the failed test, Zip, and see what we get.
Oh no! A failed test
I said in the previous section that the list of tests that passed and failed was arguably the most important page. I said arguably because it is this page, the failed test page, that will give us some good information about how to address whatever bug or bad data caused the test to fail.
On this page, the Error Message is drawn from the “error” attribute on the “message” element (who would have guessed?). This is typically a one line explanation of what went wrong. Then, the stack trace is the value of the “message” element. There is no length restriction that I know of on this value, and it can contain any valid XML characters. In my mockup, it is just a one liner, but hopefully whatever test harness is producing the output will give a real stacktrace if it exists.
<testcase name="Zip" classname="CustomerContact.Address"> <error message="Zip code out of range">Zip code 139819 is not in range of valid zip codes.</error> </testcase>
Also, you should notice From Customer Contact Tests in parenthesis at the top of this page. Interestingly enough, even though that string is defined in the “testsuite” element higher in the hierarchy, it only shows up on this page!
<testsuite name="Customer Contact Tests" tests="4" failures="1" timestamp="2012-08-23T14:40:44.874443-05:00">
Yet another thing to notice is that even though I have the tests and failures attributes in the “testsuite” element, Jenkins is smarter than displaying those values where those items are actually displayed on the page. Instead, Jenkins keeps track of how many “testcase” and “error” elements exist in the collection of tests. Pretty cool.
Conclusion
Jenkins is a really useful piece of software. However, if you’re not using a testing framework that generates valid jUnit output for Jenkins to consume, or you are building your home testing framework or harness, it can be frustrating trying to figure outhow Jenkins parses jUnit XML. Hopefully I have saved some of you the trouble of fudging around with jUnit and feeding it to Jenkins to see how it is parsed, or at least gave you a good head start.
As I become even more familiar myself with Jenkins, I’d like to elaborate on this entry as well as do some others. Jenkins is very powerful, and it is a handy tool in any programmers belt.
Hey!…. I wish I had more programmers that could implement this for me.
this is very insightful thought.
So JUnit is your testing software and Jenkins is the reporting tool?
JUnit is a Java unit test framework. There doesn’t exist such a thing for XQuery. I had to build a test harness that would output JUnit style XML, and I chose that because Jenkins supported building reports based on JUnit out of the box.
Jenkins is a continuous integration software. It enables you to build your projects and run unit tests automatically. So, for example, if you add a feature to your existing software, you’ll check that feature into your versioning system and Jenkins will check it out, build it, and run your unit tests to make sure there weren’t any regressions. It also facilitates test-driven development i.e. write a test that will define how your interface should work and then write your code such that it passes your tests.
Jenkins isn’t just for integration; it is actually your build server. Building a release for distribution on any random developer’s workstation is a no-no ?
Nice article, Nelson. I went through almost the same process as you earlier this summer.
I already had a test framework for the project I work on, which had an output in XML but which wasn’t JUnit compatible. I created an XSL stylesheet that would convert my test results into a JUnit-like output that is almost identical as yours, and configured Jenkins to apply it. This was done by selecting “Custom Tool” under the build step “Publish xUnit test result report”, and there I could specify my test results file, and the stylesheet to apply. Jenkins does the rest for you!
Just thought I’d share my experience in case someone else stumbles upon your article and needs this.
Thanks Simon. Your comment helped me.
Hi Nelson,
is there any way to send this test result report via email.
Thanks,
Dilip
Been through this loop too. Useful resource from Kohsuke you can also add attachments to your Jenkins test output. There’s also the comment that JUnit spec isnt explicitly documented : http://kohsuke.org/2012/03/13/attaching-files-to-junit-tests/
Hi.
Thanks for this good article. I wrote 2 Qt classes for writing result data. If interest drop mail.
I will add them open source repo too in near future.
br,
Erkki
Finland
Example:
testresult myRes;
myRes.SetPlanName(“MyPlan1”);
myRes.SetResultPath(“.”); // testing startss.
myRes.SetTestResult(true,”MyTest1″,”Class1Tests”,”Comment1″);
myRes.SetTestResult(true,”MyTest2″,”Class1Tests”,”Comment1″);
myRes.SetTestResult(true,”MyTest3″,”Class2Tests”,”Comment1″);
myRes.SetTestResult(false,”MyTest4″,”Class2Tests”,”Fail comment”);
Hi Erkki, i’m interested in your classes, please share it with me : [email protected] Thanks
Can u please share the source code for converting CSV to JUnit xml format ?
Hi,
Can you please share the source at: [email protected]?
Thanks,
TA
Hi,
Thanks for great sharing.
I want to ask very basic question? how to export junit output to a file in every execution. I can do it manually or by ant script but this required extra effort in each execution. is there any way, to export automatically after every single execution.?
Regards,
Muzamil
Hey. Thanks!
can you please explain how you managed to get Jenkins to separate my tests into packages instead of grouping them all as “root.”
Very helpful! Many thanks for taking effort and writing this article.
Thanks for this post Nelson! – at last I can try to sort out our custom test reporting via Jenkins
Thank you for taking the time to write this up. This is a huge help and time-saver!
Thanks for sharing!
Very helpful and clear.
Thank You!
my junit test runs in parallel. In the xml file total time is reported correct, but Jenkins shows total time as sum of each test time. How to correct that in Jenkins.
hello
I have a query.
I Need to print the Detail description of test results inaddition to passfail Status like if it is failed i Need to include the link in the test result section that navigates to another page which contain Detail Information of it. How can we add the link in the test result section in Jenkins?
Hi,
If I have some known failure in my test suite, can i disable/or remove those tests result from junit xml report .. So that it does not cause a build failure in jenkins?
Where should I put the Junit output from my test framework so that jenkins can use it?