FlexUnit for Cruise Control – XML output

If you read my earlier posts on Flex and continuous integration, you will remember that we had to do some work to get ASUnit to spit out its results in a manner that would be understood by Cruise Control. We built a log parser to parse results from the Flash players trace file into an XML file detailing the unit test results, and also a simple status file saying if the test suite succeeded or failed. Here’s how I got FlexUnit to print out results that will be understood by our parser…

I based my work here on how the flexunit.textui.TestRunner and flexunit.textui.ResultPrinter class work. I created two classes: CruiseControlTestRunner and CruiseControlResultPrinter.

CruiseControlTestRunner

This class is incredibly similar to TestRunner. In fact the only difference is that our private variable printer is set up to use our other new class – CruiseControlResultPrinter. If this variable had been declared protected, then I would have subclassed TestRunner and simply changed the setup of the printer to use our new class… You can download the file here

CruiseControlTestPrinter

The printer is a little more involved, but still not too tricky. what I do here is create an XML Object and append results as they come through. TestListeners (the interface that the printer implements) have functions that get triggered when a test starts and ends, and also if a test fails, or if there is an error. These can be used to construct our XML as the suite runs. Lets look at the class bit by bit: (or you can just download the class and get started

Constructor

Dead simple, we just initialize our XML with a top-level node:

public function CruiseControlResultPrinter()
{
	__resultXML = new XML("");
}

startTest

When a test starts, we want to append a testcase node to our XML. Each testcase node actually needs to sit in a testsuite node, so I created a helper function to automatically create a testsiute node for the testcase if one doesnt exist (ie if its the first test run in a particular Test Class):

 public function startTest( test:Test ):void
{
	var suiteNode:XML = getSuiteNode(test)
	suiteNode.appendChild(new XML(""));
	__testTimer = getTimer();
}
//------------------------------------------------------------------------------
private function getSuiteNode (test:Test):XML{
	var outNode:XML = __resultXML.testsuite.(@name==test.className)[0];
	if(outNode==null){
	outNode = new XML("");
	__resultXML.appendChild(outNode);
}
return outNode;
}

addError / addFailure

When a test fails or errors, we need to append failure details to the testcase node. The child node is named either failure or error depending on the type of problem (these nodes get displayed differently in CruiseControl). A child node populated with the stack trace is appended to the testcase node:

public function addError( test:Test, error:Error ):void{
	onFailOrError(test,error,"error");
}
//------------------------------------------------------------------------------
public function addFailure( test:Test, error:AssertionFailedError ):void{
	onFailOrError(test,error,"failure");
}
private function onFailOrError(test:Test,error:Error, failOrError:String):void{
	__suiteSuccess = false;
	var testNode:XML = getTestNode(test);
	var childNode:XML = new XML("&lt;"+failOrError+"&gt;"+error.getStackTrace()+"<!--"+failOrError+"-->");
	testNode.appendChild(childNode);
}
private function getTestNode(test:Test):XML{
	return __resultXML.testsuite.testcase.(@name==TestCase(test).methodName)[0];
}

endTest

If you look at the startTest code, you will see that we set a __testTimer variable. This comes into play in the endTest callback, where we set the execution time of the test Case:

public function endTest( test:Test ):void
{
	var testNode:XML = getTestNode(test);
	testNode.@time = (getTimer() - __testTimer)/1000;
}

print

When the test suite is complete, the TestRunner calls the print function on our class. This is actually now a much simpler affair than the one I previously wrote for ASUnit (although I will be revisiting this for ASUnit for AS3 over the next few days…). It simply traces out our generated XML, and also includes the line at the bottom specifying test Suite success (this is read by our ANT build):

public function print( result:TestResult, runTime:Number ):void
{
	printHeader(runTime);
	printMain();
	printFooter(result);
}
private function printHeader( runTime:Number ):void
{
	trace("-----------------TESTRUNNEROUTPUTBEGINS----------------");
}
private function printMain():void{
	trace(__resultXML);
}
private function printFooter( result:TestResult ):void
{
	trace("Test Suite success: "+(result.errorCount()+result.failureCount()==0)+"n");
	trace("-----------------TESTRUNNEROUTPUTENDS----------------");
}

And that’s basically it.. My next post is going to be about how I actually went about implementing these classes and got the test siute to display results differently depending on whether it was being run by the developer or as part of an ANT build…

This entry was posted in Continuous Integration, Flash & Actionscript, Test Driven Development. Bookmark the permalink. Both comments and trackbacks are currently closed.