Best way to write unit tests for dictionary - actionscript-3

This is kinda related to my other question: flex dictionary bug?
The HashMap has a method getValues() which returns an array of values:
protected var map:Dictionary = null;
public function HashMap(useWeakReferences:Boolean = true)
{
map = new Dictionary( useWeakReferences );
}
public function getValues() : Array
{
var values:Array = [];
for (var key:* in map)
{
values.push( map[key] );
}
return values;
}
In my unit test class, I have:
private var map:IMap;
[Before]
public function setUp():void
{
map = new HashMap();
map.put("a", "value A");
map.put("b", "value B");
map.put("c", "value C");
map.put("x", "value X");
map.put("y", "value Y");
map.put("z", "value Z");
}
[Test]
public function testGetValues():void
{
assertEquals(map.getValues(), /*what should I put here*/);
}
the loop for (var key:* in map) iterates the keys of the dictionary map, but it seems its implementation does it in some random way. What is the best way to write a test since I don't know what the array returned by getValues method will contain?
I thought I could do it by calling the sort method, and compare the values, is there a better way to do it?
assertEquals(map.getValues().sort(), "value A,value B,value C,value X,value Y,value Z");

Yes. If I understand your problem properly, you are trying to see if the inserted values are there in the mapValues array you are getting back. You can use the indexOf function on the array and check something like
//start a loop to go through the arrays.
if( mapValues.indexOf("value A") >= 0 )
{
continue;
}
Hope this helps!

Related

Mapping to a Variable and retrieving once they match

I have a list of check boxes that when selected the getEventName method should reurn the key that matches the label. For example if the label is "NEW", the key should be returned when map[key] = "new". These have been defined in the LABEL_EVENTTYPE function below. It is always returning an empty string and can't seem to figure out why.
public static const LABEL_EVENTTYPE_MAP:Object = {
"CANCEL":["cancelled","expired", "doneForDay"],
"NEW":["new"],
"TRADE":["trade"],
"AMEND":["replaced"],
}
private function getEventName(label:String):String{
var map:Object = ReplayConstants.LABEL_EVENTTYPE_MAP;
for each(var key:String in map){
if (map[key] == label){
return key;
}
}
return "";
}
Iterating through object properties requires for..in loop instead of for each.. in
for (var key:String in map){
if (map[key] == label){
return key;
}
}
Also take into account, that objects in your map are arrays, that's why your comparison map[key] == label will always return false.

Initialize Dictionary inline in AS3

I'm trying to find a way to initialize a Dictionary inline in ActionScript 3, like:
private var sampleDic:Dictionary = new Dictionary (
{ "zero", "Hello" },
{ "one", "World" }
);
I tried many ways, but none works. Anyone knows if it's possible, and how?
Thanks
No, you can't do it. You have to construct a dictionary and then add the values in a loop or individually.
If it's static you can do this with a block
private static var myDict:Dictionary = new Dictionary();
{
myDict["zero"] = "Hello";
myDict["one"] = "World";
}
If you REALLY want something like that, you can use a Dictionary factory:
public class DictFactory
{
public var dict:Dictionary;
public function create(obj:Object):Dictionary
{
dict = new Dictionary();
for (var key:String in obj) {
dict[key] = obj[key];
}
return dict;
}
}
Usage:
private var sampleDic:Dictionary = new DictFactory().create({ "zero":"Hello", "one": "World" });
The DictFactory.create expects a Object with key-values, that will be applied to the returned Dictionary, if you pass any other object (in AS3, any class is Object), results may be undesireable. :)
You can extend dictionary class and override default constructor with one that accepts initial key-value pears.
EDIT:
You can also use this dirty JS like solution :)
import flash.utils.Dictionary;
var dict : Dictionary = function ( d : Dictionary, p : Object ) : Dictionary { for ( var i : String in p ) { d[i] = p[i] }; return d; }(new Dictionary(), {
"zero": "Hello",
"one": "World"
})
trace( dict["zero"] );
If there is no specific reason to use a Dictionary you can do it with an object.
private var sample:Object = {
"zero": "Hello",
"one": "World"
};

flex dictionary bug?

I was testing a HashMap implementation in AS3 .
I have tried the following code:
var map:IMap = new HashMap();
map.put("a", "value A");
map.put("b", "value B");
map.put("c", "value C");
map.put("x", "value X");
map.put("y", "value Y");
map.put("z", "value Z");
Then I called the clear() method:
map.clear();
The size of the hashmap didn't become 0, but it was 1. The problem is that when the key is "y", it doesn't get removed.
The corresponding code is as follows:
protected var map:Dictionary = null;
public function HashMap(useWeakReferences:Boolean = true)
{
map = new Dictionary( useWeakReferences );
}
public function put(key:*, value:*) : void
{
map[key] = value;
}
public function remove(key:*) : void
{
map[ key ] = undefined;
delete map[ key ];
}
public function clear() : void
{
for ( var key:* in map )
{
remove( key );
}
}
If I call the clear() function again, the remaining key will be removed:
if (size() != 0)
{
clear();
}
Does anyone know what's the reason the y key doesn't get deleted?
I haven't had the time to look at the Dictionary implementation in tamarin (the VM for flash), but it seems that the Dictionary is beeing rehashed when a value is reaffected to the map by the line map[ key ] = undefined; in the remove function.
I.E. you begin an iteration with a set of key but then a rehashed occured and the keys are not valid anymore and the VM can't find the previous key and so in this case miss the y key.
What you can do is remove the map[key] = undefined; from the remove function and it should work. What it's strange is that the delete didn't produce any similar bug...
To show that the rehash has occurred see here a live example :
http://wonderfl.net/c/2PZT
You will see that a key is iterate twice when you assign a value to the dictionary.

Array.filter to dynamically test values

this is from Adobe docs:
package {
import flash.display.Sprite;
public class Array_filter extends Sprite {
public function Array_filter() {
var employees:Array = new Array();
employees.push({name:"Employee 1", manager:false});
employees.push({name:"Employee 2", manager:true});
employees.push({name:"Employee 3", manager:false});
trace("Employees:");
employees.forEach(traceEmployee);
var managers:Array = employees.filter(isManager);
trace("Managers:");
managers.forEach(traceEmployee);
}
private function isManager(element:*, index:int, arr:Array):Boolean {
return (element.manager == true);
}
private function traceEmployee(element:*, index:int, arr:Array):void {
trace("\t" + element.name + ((element.manager) ? " (manager)" : ""));
}
}
}
The problem is the Array class filter method. It works this way: you pass a function as an argument of filter and an array is returned based on the function you pass. The problem is that it seems you can't add any other parameter. So, if you must create (for example inside a for loop) 4 arrays from the same array and you want to use the same function, you can only test against a property of the class you must previously set to the value you want to test.
Is there any other way to add that parameter?
When filtering by a variable object properties in an array, I wrapped the filtering into another function:
protected function FilterByProperty(input_array:Array, extra_testing:Object):Array
{
function FilterFunction(element:Object, index:int, array:Array):Boolean
{
return element.property == extra_testing; // Arbitrary test
}
return input_array.filter(FilterFunction);
}
var filtered_array:Array = FilterByProperty(unfiltered_array, test_property);
You can use second parameter of filter() method to pass data to filter function. It will be avaliable as "this" variable. To do that first parameter must be a closure, not a class method.
var array:Array = [...];
var params:Object = {"param1": value1, "param2": value2};
var filterFunction:Function = function(item:*, index:int, array:Array):Boolean {
var param1 = this["param1"];
var param2 = this["param2"];
};
array.filter(filterFunction, params);
You want to use something like Delegates or function binding, or closures. Depending on your coding and terminology preferences. The idea behind all of them is that you create a dynamic function wrapper for the core "filter" function. That wrapper will have access to extra parameters that you pass. So, the first time you call it, you might go:
a.filter(Delegate.create(myFunc, param1));
and the next time:
a.filter(Delegate.create(myFunc, param2));
and you function would have something like this:
private function myFunc(item:*, index:Number, a:Array, param:Object=null):Boolean{}
A quick an dirty method is to just pass an inline function like this:
a.filter(
function(item:*, index:Number, a:Array):Boolean {
return myFunc(item,index,a,param1);
}
);
where param1 is passed using the closure created by the function definition.

Trying to understand the AsyncToken in Flex/Actionscript

I am trying to understand the way the AsyncToken works in actionscript. How can I call a remote service and ensure that a specific parameter is available in the result or fault event functions? I think it is the async functionality I want to use.
The following code will hopefully explain what I am trying to do. Feel free to modify the code block as your explanation.
Thanks.
public function testSerivceCall(data:Object, callBackCommand:String):void
{
// Assume callBackCommand == "FOO";
// How can I pass in callBackCommand as a parameter to the result or fault events?
// How do I create an async token here?
var remoteObject:RemoteObject;
remoteObject = new RemoteObject();
remoteObject.destination = "zend";
remoteObject.source = "MyService";
remoteObject.endpoint = "http://example.com/service";
remoteObject.test.addEventListener(ResultEvent.RESULT, _handleTestResult);
remoteObject.test.addEventListener(FaultEvent.FAULT, _handleTestFault);
remoteObject.test(data);
}
private function _handleTestResult( event:ResultEvent ) : void
{
// How do I get the async token value?
// How can I get the value of callBackCommand in this code block?
if (callBackCommand == "FOO")
{
// do something related to "FOO"
}
else
{
// do something else with the result event
}
}
private function _handleTestFault( event:FaultEvent ) : void
{
// How do I get the async token value?
// How can I get the value of callBackCommand in this code block?
}
An edit to make this question more clear:
Assume I make the following method call somewhere in my code:
testSerivceCall(personObject, "LoginCommand");
How do I get access to the actual string "LoginCommand" inside the _handleTestResult function block?
The reason I want to do this is because I want to dynamically call back certain functions and hand off the result data to specific commands that I know ahead of time when I am making the service call.
I am just having a time grokking the AsyncToken syntax and functionality.
I did not even need closures. I added a class as below which I called externally.
The call was like this:
public class MyClass
{
...
var adminServerRO:AdminServerRO = new AdminServerRO();
adminServerRO.testSerivceCall("FOO",cptyId);
}
public class AdminServerRO
{
private function extResult( event:ResultEvent, token:Object ) : void
{
//the token is now accessed from the paremeter
var tmp:String = "in here";
}
private function extFault( event:FaultEvent ) : void
{
var tmp:String = "in here";
}
public function testSerivceCall(callBackCommand:String, cptyId:String):void
{
var remoteObject:RemoteObject = new RemoteObject();
remoteObject.destination = "adminServer";
var token:AsyncToken = remoteObject.getCounterpartyLimitMonitorItemNode(cptyId);
token.addResponder(new AsyncResponder(extResult,extFault,cptyId));
}
}
While the accepted answer will accomplish what the original submitter wants it does not actually answer the question which was asked. An AsyncToken is created as a result of a remote method call and is accessible from the ResultEvent. Since AsyncToken is a dynamic class you can add whatever property to it that you want. The code below should demonstrate this:
public function testSerivceCall(data:Object, callBackCommand:String):void
{
var remoteObject:RemoteObject;
remoteObject = new RemoteObject();
remoteObject.destination = "zend";
remoteObject.source = "MyService";
remoteObject.endpoint = "http://example.com/service";
remoteObject.test.addEventListener(ResultEvent.RESULT, _handleTestResult);
remoteObject.test.addEventListener(FaultEvent.FAULT, _handleTestFault);
var token:AsyncToken = remoteObject.test(data);
token.callBackCommand = callBackCommand;
}
private function _handleTestResult( event:ResultEvent ) : void
{
if (event.token.callBackCommand == "FOO")
{
// do something related to "FOO"
}
else
{
// do something else with the result event
}
}
private function _handleTestFault( event:FaultEvent ) : void
{
//event.token.callBackCommand should be populated here too
}
If you want to access the properties used during the remote call (parameters to the call and/or AsycToken), you can make use of closures. Just define the result event handler inside the calling method as a closure. It can then access any variable in the calling function.
public function testSerivceCall(data:Object, callBackCommand:String):void
{
var _handleTestResult:Function = function( event:ResultEvent ) : void
{
// token is visible here now
if (callBackCommand == "FOO")
{
// do something related to "FOO"
}
else
{
// do something else with the result event
}
}
var remoteObject:RemoteObject;
remoteObject = new RemoteObject();
remoteObject.destination = "zend";
remoteObject.source = "MyService";
remoteObject.endpoint = "http://example.com/service";
remoteObject.test.addEventListener(ResultEvent.RESULT, _handleTestResult);
remoteObject.test.addEventListener(FaultEvent.FAULT, _handleTestFault);
var token = remoteObject.test(data);
}
If I'm reading your question correctly, you're trying to figure out how to access the actual data returned by the ResultEvent ?
If so, assuming you've made the call correctly and you've gotten data back in a format you're expecting:
private function _handleTestResult( event:ResultEvent ) : void
{
// you get the result from the result property on the event object
// edit: assuming the class Person exists with a property called name
// which has the value "John"
var person : Person = event.result as Person;
if (person.name == "John")
{
Alert.show("John: " + person.name);
}
else
{
Alert.show("Not John: " + person.name);
}
}
private function _handleTestFault( event:FaultEvent ) : void
{
// Maybe you know the type of the returned fault
var expectedFault : Object = event.fault as MyPredefinedType
if (expectedFault.myPredefinedTypesPredefinedMethod() == "BAR")
{
// something here
}
}
The ResultEvent has a property called result which will hold an instance of the object returned by the result (it might be the output of an XML file if using a web service, or a serialized object if using AMF, for example). This is what you want to access. Similarly, FaultEvent has a fault property that returns the fault information.
Edit: Changed code in _handleTestResult() in response to Gordon Potter's comment.