ActionScript: Just different enough to piss you off

Brian LeGros | October 19th, 2008 | programming  

So I’ve started my new job and I’m working with Flex and ActionScript 3. I was tasked with throwing together a couple classes and there respective unit tests this week. The classes don’t integrate with any external resources and mostly do pass through except for some cross cutting concerns (e.g. - logging, security, etc). I figured writing my tests would be pretty straight forward: throw together the appropriate test cases and in each mock out any dependencies.

Now when I think about a language I try to put it into the context of other languages I’ve had experience with, identify differences in language elements, and work from there. With respect to ActionScript, I tended to problem solve in the context of JavaScript since they are both based on ECMAScript, but I’ve run into some frustrations along the way.

To mock out an object in a test using JavaScript I’d typically write something like:

1
2
3
4
5
var dependency = new MyDependency();
dependency.methodToMock = function () { return true; };
 
var obj = new MyClass(dependency);
assertTrue(obj.passThroughMethodUsingMockThatICreated());

or if I wanted to mock out objects being instantiated by a dependency that I couldn’t inject:

1
2
3
4
MyDependency.prototype.methodToMock = function () { return true; };
 
var obj = new MyClass();
assertTrue(obj.passThroughMethodUsingMockThatIDidntCreate());

In ActionScript, assuming that MyDependecy is declared in an ActionScript class, the first and second approach I used in the above will not work. I did some reading on the Flex 3 livedocs and found out that the culprit was the traits object that was introduced in AS3. To abridge, the traits object is a copy of all applicable properties from parent classes and defined properties in the current class that need to be available at run-time to help reduce the performance hit of walking the prototype chain previously used for inheritance. The traits object is an internal mechanism which is not accessible by the developer. Prototype-base inheritance is still available, but falls secondary to traits. Awesome.

So unless I declare the methods of my class as members of the prototype for the class, I can’t manipulate them at run-time. So instead of the following:

1
2
3
4
5
public class MyDependency {
   public function methodToMock() : Boolean {
      ...
   }
}

I’d have to write:

1
2
3
4
5
public class MyDependency {
   prototype.methodToMock = function() : Boolean {
      ...
   }
}

So now I’ve got methodToMock available in a data structure I can mess with at run-time. I still, however, can’t accomplish what I did in JavaScript unless I add the keyword dynamic to the class declaration (as below) or remove the “strict” flag from the compiler:

1
2
3
4
5
public dynamic class MyDependency {
   prototype.methodToMock = function() : Boolean {
      ...
   }
}

Now I can manipulate the object and its class’ prototype the way I would in JavaScript. It irks me a bit that performance was the driving force for all of this hoopla, but I’m just an application developer, so I won’t pretend to understand all of the motivations for these decisions.

As another alternative, I thought about just using Object for all of my mocks, but I quickly dismissed the option since we’re using the explicit typing with ActionScript, as I suspect most are since its promoted for its performance boost. I also looked into using the Proxy class as a run-time solution, but alas it can’t be instantiated, only extended; this would mean writing class declarations for every mock I wanted to create, which seems kinda silly, and I still would have to deal with the explicit typing issue. On a side note, it would have been nice if Proxy had the option to be instantiated with the object it’s proxying as a constructor argument, mimic the type passed as the argument, and allow run-time manipulation. No idea if this is feasible though, so I’ll count it as a pipe dream for now. In any case, from my experience mocks are intended to be used within a context and disposable for the purpose of testing; why would I write anything more than inline code to utilize mock objects?

In the end, I’m probably going to be making end-to-end tests using stubs for the classes I want to test. Once we have some time to evaluate mock object libraries for ActionScript, I’ll probably start digging again for more answers. We’ll eventually need something to help us with expectation-based testing anyway, so a library will be the way to go. Hopefully I’ll end up finding something more like Mocha or Moq than JMock, although I think the opposite may be true.

There are language elements in ActionScript that I’m extremely grateful to have at my disposal (e.g. - events, properties, functions, etc). A few quirks exist though (e.g. - object/class manipulation at runtime and reflection) that still get me when I try to think about ActionScript in the context of JavaScript. Going forward I think I’m going to switch gears and solve problems more as I would a Java/C# developer than a JavaScript developer, favoring the use of libraries over the language features to get every day tasks done. I like ActionScript, but what good would a blog be if I couldn’t complain.

:)

NOTE: I can’t take credit for the tagline, some Flex developers I know have begun to adopt it and I borrowed it from Dan.



Tags: , , , ,

Related posts

Discussion

  1. Josh Tynjala Says:

    Your solution is correct, but your explanation for why you need that solution is wrong. Traits have nothing to do with it. That’s just an implementation detail for how Adobe optimized their runtime, and I don’t know why they decided to share it.

    According to the (now-former) ECMAScript 4 specification (which also would have driven the next version of JS), classes are “sealed” by default. This means that you cannot add properties or methods at runtime. As you discovered, you can change this behavior by making the class dynamic. Let me say again that traits are simply the way that Adobe optimized performance based on what’s in the spec. Even if they didn’t use the these traits in their runtime, classes still would have been sealed.

    Now that that’s cleared up… :)

    In the end, I think you generally shouldn’t think of AS3 from the perspective of a JS developer. It’s a much stricter language that, in many ways, is closer to Java. All the fun little prototype and dynamic tricks JS developers and (AS1/AS2 developers) are used to can be a lot harder to do in AS3. On the other hand, as an AS3 developer, I recently spent some time learning more advanced JS techniques and brought them back to AS3 with great success.

  2. Brian LeGros Says:

    @Josh - Thanks for stopping by and clearing up my misunderstanding. I’m not up on all of the latest activity with ECMAScript, so I appreciate the clarification. Based on what you’re saying, I hope “sealed” classes are not part of ECMAScript 3.1 so I can back to my prototype tricks :) In any case, I’d be really interested in hearing what you were able to bring over from the JavaScript side into ActionScript. I’d really dislike the idea of working with ActionScript like I would Java, so any insight is appreciated.

  3. kim Says:

    *twitch*

  4. Elaina Says:

    Turns out Joel did tell me that you got the job (lies and deceit)… lol congrats!

    (I don’t understand anything else in this blog except the first sentence… you programming geeks..)

  5. zwetan Says:

    I understand the tag line for Flex but that does not necessary apply to AS3.

    Besides Mock, you can use Fake, and for that AS3 is pretty good in the way it handle upcasting and downcasting

    MyClass to test

    public FakeMyClass extends MyClass

    //in your unit tests or other tests
    //easy downcasting
    var obj:MyClasss = new FakeMyClass();

    so ok, you have to implement a class but it’s working pretty neat as it also keep the typing.

  6. Brian LeGros Says:

    @zwetan - Thanks for stopping by. In the context of your comment, fake classes are what I refer to as stubs. The reason I prefer mocks to stubs is because of the context specific nature of each test that I right. If I write a stub to use in my test case and a method I’m testing has 3 or 4 possible outcomes, I’ve got to program that into my stub, or write a fresh one each time. Stubs in the example you gave are great to manage typing, but I find it makes it harder on testing in the long run, because you have to write/maintain a lot of the same code in different permutations.

Add A Comment