views:

254

answers:

2

The actionscript I want to write looks like this:

public function API(requestClass:Type=URLLoader) {
  var req:URLLoader = new requestClass(new URLRequest("some url"));
  req.load(url);
  //etc
}

so that I can test the API class by passing in a mocked subclass of URLLoader. This doesn't appear to be possible in Actionscript's type system.

Alternatively, it could be sufficient to change the URLLoader's load() method at runtime. I had high hopes for this code in a test method:

var b:Array = [];
URLLoader.prototype.load = function(u:URLRequest):void {
  b.push(u);
}
(new URLLoader()).load(new URLRequest("http://localhost"));
assertEquals(b.length, 1);

but URLLoader does in fact call the url it's given, and b.length == 0.

So! Is there any way that I can write my API class to be testable without putting the testing logic within my API class? Have I missed something obvious?

+1  A: 

Take a look at the flash.utils package. Particularly, the function: getDefinitionByName(). This link has a nice example of reflection that you may like. You can give this a try (after suitable modifications, of course):

public function API(mock:Object, type:String) {
    var ClassReference:Class = getDefinitionByName(type) as Class;
    var instance:Object = new ClassReference();
//instance.load(url); -- play around with your new class!

}
dirkgently
Couldn't you just have the type variable be of type Class and skip that first step?
Herms
+2  A: 

There are a couple of errors in the code you have provided, nontheless the concept works perfectly in AS3. The following code, for example, compiles and runs flawlessly:

package {
    import flash.display.Sprite;

    public class Main extends Sprite {
     public function Main():void {
      trace(getDynObj());
      trace(getDynObj(Number));
      trace(getDynObj(String));
     }

     public function getDynObj(requestClass:Class = null):* {
      var req:* = new (requestClass || int)("16.51");
      return req;
     }
    }

}

and outputs:

16
16.51
16.51

So, your function needs the following modifications:

public function API(requestClass:Class = null):void {
    var req:* = new (requestClass || URLLoader)(new URLRequest("some url"));
    //etc
}

Note: URLLoader does not accept a plain string as its constructor argument, you must wrap the string in a URLRequest object.

David Hanak
The Class class was exactly what I was looking for, I guessed it too but was thrown off by the fact that you can't have a default parameter for a Class ( http://is.gd/l8p9 ). I'm aware of how to build a URLLoader, I was just writing pseudocode, but I've updated the post.
llimllib
Wow, did not know about the || operator being possible inside of a new operator!
Karthik