Testing a Model Robotlegs EventDispatcher In FlexUnit
First off, what makes a model a Robotlegs model? There is a base class in Robotlegs which is called Actor. By extending Actor, generally used in models and services in Robotlegs applications, we are able to access a property of Actor which is named eventDispatcher. EventDispatcher is shared throughout the Robotlegs framework, thusly, any events dispatched upon the eventDispatcher may be heard across the entire application. It is not required to extend Actor in a Robotlegs application. Actor exists as a helper, should you need to be able to dispatch an event and have it heard anywhere in the application. However, if you do not need to dispatch an event from a particular model, there is no obligation to extend Actor (it is recommended that you don’t in this case). Additionally, you may, of course, dispatch an event on the standard Flex framework and not create the dependency on Robotlegs. You might choose to do this if you plan to use your code in other framework-agnostic applications.
About models and services from ActionScript Developer’s Guide To Robotlegs:
They don’t listen, they only talk
Models and services are like the worst kind of dinner party guest. They have no interest in what other people have to say. All they do is send messages out to the framework–they never listen directly to their eventDispatcher property to see what other classes are saying.
Aside: If you haven’t read ActionScript Developer’s Guide To Robotlegs yet then stop right here. Over on the right you will find a link to the books site on O’Reilly Media, go get it NAO!!
Okay, the purpose of a model, in general, is to provide an API which preserves state in an application. A model is updated with state data that then may be used by other classes in your application. However, we don’t necessarily want to tightly couple our model to other classes. The best way to decouple our model is by dispatching events that may be heard by any classes that are interested in knowing that state data has changed. Enter Actor. By extending Actor in our model we now have access to the application’s framework eventDispatcher.
Here is the scenario: A service call requests some data from some remote data source (e.g. database, Webservice, HttpService, etc…). The result event of the service receives the data in the result and passes it to a factory class where it builds the resulting data into an object that we can use. The factory updates the model’s property that we are interested in and notifies the service where we can do whatever else we need to do. The important part here is that the model gets updated. We could dispatch an event from the factory or service that the model has been updated, but we really don’t know 100% from these classes that the model data is actually updated. The only place we can dispatch this event with complete certainty that the data was updated in the model is from the model itself.
Here is the model’s mutator that we will be testing:
public function set myObject ( myObject : Object ) : void
{
this._myObject = myObject;
eventDispatcher.dispatchEvent ( new MyEvent ( MyEvent.MODEL_UPDATED, false, false, myObject ) );
}
A benefit of accessors (getters) and mutators (setters). By using getters and setters in our model we create a public API. Additionally, by virtue of the setter method, we have a distinct method where a property gets set/updated. It is within the mutator that we can dispatch an event when we know for certain that the property has been updated. We also have public methods that we can test, such as testing state data is what it is supposed to be, and in our case for this example that an event is dispatched when state data changes.
The way I approach this is to simply check that the event dispatches when I provide the model’s property with a new value. We can add an event listener in the test whose handler method confirms that the event dispatched. Then we can assert that the result of the handler method is the same as what we expect. In this case, I increment a counter from an anonymous function if the event listener hears the event and then assertEquals. If the counter and expected value are equal, then our test passes. Otherwise, the test fails and we know there is a problem.
[Test]
public function testMyModelSetMyObject_dispatchEvent_MyEventIsDispatched():void
{
var count:Number = 0;
instanceModel.eventDispatcher.addEventListener(MyEvent.MODEL_UPDATED, function (event : MyEvent):Number { return count++;} );
instanceModel.myObject = MY_OBJECT;
assertEquals(1,count);
}
A bit of convention explained here. The test name provides for three distinct pieces of information that will be valuable to yourself and anyone who later is maintaining your tests. A test class name provides the following information:
- The name of the method under test
- The state, or condition, which is under test
- The expected behavior of the test method
I then declare an instance counter that will be used to assert against. The instanceModel is the model that we are testing, by setting an eventListener on its eventDispatcher we can listen for the specific event for which we are interested. In this case, I want to know if the event/type, MyEvent.MODEL_UPDATED is dispatched when the myObject property of the model is updated. For ease of implementation, I use an anonymous function which takes the MyEvent type as an argument and returns a number to increment a counter and return its value. Then, I updated the model’s property, which should dispatch the event. Lastly, I assertEquals that the counter value is 1 as expected, meaning that the event dispatched, was heard in the anonymous handler method and incremented the counter.







