Unit 9 - External Data

Download Download Unit Project Files

For the sake of optimization or integration, large or complicated sets of data are often easier to manage within external files. FlexUnit 4.x provides support for external data in Theories as well as Parameterized tests, which you will learn to use in Unit 11.

Objectives:

After completing this lesson, you should be able to:

Topics

In this unit, you will learn about the following topics:

Understanding asynchronous loading

The large collections of data points that accompany theories can become increasingly difficult to maintain as the number of tests in your system grows. Additionally, in some circumstances, you may want to generate the data used for your data points in a different application or system.

In these cases, you will want to switch to external data. External data has the advantage of being maintained external to the tests, making maintenance easier and increasing the legibility of test cases.

However, external data presents a particular problem in Flash-based testing. Flash is a single-threaded machine that uses asynchronous communication for all remote data access. It does not have the ability to access data synchronously nor the ability to suspend a process until data arrives.

This means the test runners will need to wait for a server response before parsing the returned data into datapoints before beginning the test process.

While you could load all of the data for your system external to the testing framework before testing ever begins, you now lose some of the flexibility, or at least the performance, inherent in the ability to pick and choose test cases or suites and only load the data that is required.

To resolve these issues, FlexUnit 4 offers the concept of External Dependency Loaders. An External Dependency Loader allows the loading of external data in a test.

Loading external data

As every source of data for every user of FlexUnit is slightly different, you are required to write a simple class that understands how to load and parse your test data. This class implements the IExternalDependencyLoader interface and is used to load all data into FlexUnit.

This interface has one required method: retrieveDependency(). This method returns an ExternalDependencyToken. During startup of the testing process, FlexUnit looks for IExternalDependencyLoaders and invokes this method.

Internal to this method, you are responsible for making any server calls and loading any data. Once the operation has been completed, in either a result or fault, you are responsible for notifying the framework that it is now safe to begin testing.

Here is an example of the retreiveDependency function and the associated result and fault handlers that uses an HTTPService to retrieve data:

public function RadiiDataHelper( url:String ) {
	service = new HTTPService();
	service.url = url;
	service.resultFormat = "e4x";

	dToken = new ExternalDependencyToken();
}

public function retrieveDependency( testClass:Class ):ExternalDependencyToken {
	var asyncToken:AsyncToken = service.send();
	asyncToken.addResponder( this );

	return dToken;
}

public function result( data:Object ):void {
	var ar:Array = new Array();
			
	var list:XMLList = data.result..node;
	var node:XML;
			
	for ( var i:int = 0; i < list.length(); i++ ) {
		node = list[ i ][ 0 ];
		ar.push( Number( node.text() ) );
	}
			
	dToken.notifyResult( ar );
}

public function fault(info:Object):void {
	dToken.notifyFault( "Totally broken" );
}

In this loader:

  1. Loader is instantiated with a new HTTPService.

  2. retrieveDependency() makes the external HTTP call, returning the loaders ExternalDependencyToken.

  3. If the call is successful, the result function parses the data into an array (datapoints expect an array) and calls notifyResult() on the ExternalDependencyToken passing the datapoint array.

  4. If the call fails, the fault function calls notifyFault() on the token, passing a useful error message.

  5. If neither notifyResult() nor notifyFault() are called on the token the runner will stall indefinitely as it is impossible for the testing framework to identify a reasonable delay for your situation. This makes it extremely important to handle both cases.

To use an external loader you must instantiate the loader and assign it to a public static variable in the test case. Constructor args are used to provide any additional configuration information to the loader.

public static var testLoader:IExternalDependencyLoader = new MyLoader(" myData.xml" );

You then need to create the datapoints array. However, instead of instantiating the array, as you had in previous lessons, you will specify a loader argument on the DataPoints metadata tag. The runner knows how to handle this attribute and ensure your loader is invoked.

[DataPoints(loader="testLoader")]
[ArrayElementType("Number")]
public static var radii:Array;

Once the data points are loaded, your test case will use them as if they were statically coded into the test case.

Walkthrough 1: Moving Theory Data to an External Source

In this walkthrough you will perform the following tasks:

Load the external data

  1. Import the FlexUnit4Training_wt1.fxp project from the Unit 9/Start folder. If you completed the previous walkthrough, choose the option to Overwrite your existing project. If this is your first Flex Unit 4 Training walkthrough, import it as a new project. Please refer to Unit 2: Walkthrough 1 for instructions on importing a Flash Builder project.

    The new FlexUnit4Training_wt1 project contains everything that all the walkthroughs up until this point have taken you through.It also includes several new files and additional dependencies to help you through the current walkthrough.

  2. Open the CircleTheory.as file in the math.testcases package within the tests directory.

  3. Add a public static variable named radiiLoader of type RadiiDataHelper to the CircleTheory class. It should instantiate a RadiiDataHelper object with the argument "xml/radii.xml".

    public static var radiiLoader:RadiiDataHelper 
    	= new  RadiiDataHelper( "xml/radii.xml" );
    

    If you did not use code-completion, add the import for helper.RadiiDataHelper at this time. RadiiDataHelper is the external loader and already has the logic for loading the data.


    Alter the array to use the external data

    Now that you are loading the radii data from an external source the radii variable no longer requires the static values. The radii variable must also be marked with [DataPoints(loader="radiiLoader")] metadata.

  4. Modify the radii array so that it has [DataPoints(loader="radiiLoader")] metadata.

    [DataPoints]
    [ArrayElementType("Number")]
    public static var radii:Array = [ -5, 1,2,3,4,5,6,7,8,9,10 ];
    

    Becomes:

    [DataPoints(loader="radiiLoader")]
    [ArrayElementType("Number")]
    public static var radii:Array;
    
  5. Save CircleTheory.as.


    Examing the loader class

  6. Open the RadiiDataHelper.as file in the helper package within the tests directory.

    To get an idea of how the loader class retrieves the data, you are going to setup breakpoints in the RadiiDataHelper class. As a result, you will get a chance to examine the data before it is parsed into the theory.

  7. In Flash Builder add a breakpoint within the RadiiDataHelper constructor at the line that reads: dToken = new ExternalDependencyToken();

    AddingBreakpoint

    Figure 1: Adding a breakpoint to the constructor

  8. Add another breakpoint within the retrieveDependency() method at the line that reads var asyncToken:AsyncToken = service.send();

    AddingBreakpoint

    Figure 2: Adding a breakpoint to the retrieveDependency() method

  9. Add a breakpoint within the fault() method at the line that reads: dToken.notifyFault( "Totally Broken" );

    AddingBreakpoint

    Figure 3: Adding a breakpoint to the fault() method,

    Ideally, you shouldn't hit the breakpoint within the fault() method, but if the loader has a problem retrieving the data, you will know before the tests are even run.

  10. Finally, add a breakpoint within the result() method at the line that reads: dToken.notifyResult( ar );

    AddingBreakpoint

    Figure 4: Adding a breakpoint within the result() method

  11. Switch Flash Builder over to debug view using the selector in the upper-right-hand corner.

    FlashDegub

    Figure 5: Flash Debug view

  12. In the expressions tab, which should appear in the upper-right-hand corner by default, add a new watch expression by right-clicking on the whitespace.

    WatchExpression

    Figure 6: Adding a watch expression

  13. The expression being watched should be list, which is the returned data converted to an XMLList object.

    NewWatch

    Figure 7: New watch expression

  14. Add another watch expression for the ar variable, which is the parsed array that the RadiiDataHelper returns as data points.

  15. Run the FlexUnit4Training.mxml file in debug mode.

If the FlexUnit4Training.mxml file runs successfully, it should take you through each breakpoint except for the one placed in the fault() method.

When you reach a breakpoint, press F8 or the play button to move on.

The first breakpoint illustrates the instantiation of the ExternalDependencyToken, which is responsible for notifying FlexUnit whether the data is return or there is a fault.

The next breakpoint illustrates the sending of the HTTPService.

When you reach the third breakpoint, on the dToken.notifyResult( ar ); line, the list watch expression should show that list is equal to an XMLList whose values can be expanded and evaluated in the Expressions tab.

ExpressionList

Figure 8: The list expression in the Expressions tab

The ar expression is the parsed array created in the result() method from the XMLList.

ArExpression

Figure 9: The ar expression in the Expressions tab

After running through the last breakpoint, you should see the following in your browser window.

TestsPassed

Figure 10: FlexUnit tests passed

The radii.xml file contains the same values previously declared within the radii array. There is no change in operation, because the externally loaded values are interacting with the test in the same way, but having the data externalized makes it easy to modify the tests to run with different values, without having to re-compile the tests.

Summary

Navigation