|
|
|
With AS1 and AS2 creating these dynamic proxies was no problem since you had complete control over the prototype of an object. However in AS3 things have become much stricter and many of the dynamic aspects of ActionScript are gone.
The only way I see a possibility in AS3 to mock a strongly typed object to a class or an interface is to have a method built into the Flash Player that allows this or a low level library like ASM and CGLIB for Java that allows bytecode manipulations. I really think that with the growth of Flex in the industry and the fact that it is now approached by many developers coming from a Java/.net background, this will soon become a need for many developers working on enterprise projects. Not being able to implement interfaces inline strongly limits the testing capabilities in AS3.
In java, f.i., you can instantiate an interface like this: new MyInterface() { myMethod() { //do stuff}} which is very helpful during testing. For this reason I think that is extremely important that there is an easier way to implement mock objects. Proxy objects are a very good way to do this. This would be a very good addition. The other day I was thinking in how great would be to get CGLIB functionality in AS3 so we can use projects like prana load XML configuration files and reduce the number of clases the developer must code. I wasn't aware of this limitation, so we can't do nothing about this at this moment. Hope this will be taken into account for ASTRO ;)
Between the ActionScript Proxy class and the dynamic keyword, it looks like the language is 90% there.
I'd love for this code to compile: public dynamic class DelegateUIComponent extends Proxy implements IUIComponent { private var selector : Function; private var cached : UIComponent; public function DelayedUIComponent(selector : Function) { this.selector = selector; } flash_proxy override function callProperty(name:*, ...rest):* { if (!cached) { cached = selector() as UIComponent; } return (cached[name.toString()] as Function).apply(cached, rest); } flash_proxy override function getProperty(name:*):* { if (!cached) { cached = selector() as UIComponent; } return cached[name.toString()]; } flash_proxy override function setProperty(name:*, value:*):void { if (!cached) { cached = selector() as UIComponent; } cached[name.toString()] = value; } } The code would compile except that I don't *at compile time* implement the interface methods and properties. But to my mind, since the class is declared dynamic, I should be able to defer that implementation. Furthermore, when I extend Proxy, I automatically implement all these methods in a very generic way so it seems that should be enough to placate the interface requirements. Obviously the 'selector' variable above is a function which would return a reference to a UIComponent when invoked.
We need Dynamic Proxies for mocks otherwise Flex can not become an Enterprise technology !
I've been reading about Mock testing. It seems there is another route which we might be able to use... Class remapping. If the Flash Player had something like java.lang.Insturment would that be a sufficient way to do Mock testing?
I very want this feature, in other case AS avoid greatest ability...
Response to James Wards comment:
If I'm not mistaken Class remapping will change the class defenition of a certain class, and therefore it will affect all instances of this class. It is needed for mocking if you cannot inject (set) the mock on the object that you are testing, but it is in itself not a solution for the current issue. In mock testing most of the time you create a (mock)object that implements an interface (or is a subclass of an actual class) and modify/create behavior on just that one instance before handing it to the class under test. Remapping existing classes it not needed in that scenario, but being able to dynamically implement an interface is. Thanks Simon. I'm working on a method for doing this using the EvalES4 library. It's not pretty but it might be a temporary way to make this work until Flash Player adds the native method. I'll update this bug when I get something functional.
James, I've been planning to do the exact same thing, want help? josh@gfunk007.com
@James: I have been playing with the EvalES4 lib some months ago but didn't succeed in created typed proxies. I cannot remember why exactly, but I think it had something to do with the async behavior of the dynamic class loading. I'm curious on your progress. Please keep me updated and let me know if I can help.
Proxy is already magical (ie there must be special case code for it in Player), so basically, all we need is two changes:
- Compiler lets us cast a Proxy to whatever we want (tiny change) - Proxy gets a method like flash_proxy::canBeCastAs(targetClass : Class) : Boolean (this will obviously require changes to Player) We know the AVM2 gets updated, as there's a bunch of new opcodes specifically for Alchemy that some ninja on the haXe team has discovered and documented, so I guess we just need to get enough support and votes for this feature :D Hi Josh,
That is a great idea! Also, I've looked further into the EvalES4 thing mentioned above but it doesn't seem to be possible since: - Currently packages aren't supported - It doesn't seem possible to use custom (or Flex) classes in the new to-be-eval'd class because the compiler can't find the class definitions or something. Some hopefully we can get something like Josh's suggestion into an upcoming FP release... A full blown real reflection API would be nice too. -James Someone mentioned anonymous types as well up there in the comments list and that deserves my vote alone. Still dynamic proxy generation is a must for any serious programming language... as is full generic support, metadata, autoboxing, abstract classes... but I digress.
The entire reflection API in AS3 should be rewritten to imitate Java's. Class objects are weak in terms of power, you can't do anything with them. At least Java has a Class object that defines methods like "getFields()" and "getMethods()". A really powerful reflection API built on a language is no substitute for having that reflection API built right into the language. Please give us this? Oh, and my wish list is as follows: Anonymous types Static imports Autoboxing/Unboxing for primitive types An extensible metadata system True generic support cross-language The elimination of the redundant "function" keyword Static methods defined in interfaces Abstract types (c'mon, it's easy to do!) If you don't meet these demands in 2 weeks.... ;) The asmock sourceforge project implements dynamic mocking via loadBytes. It has limitations (that I have posted about on StackOverflow), but generally works well with mocking interfaces/classes. The public API is actually a port from Rhino Mocks, a record/replay/verify mocking framework for .NET.
Wow Richard! Cool stuff! How are you generating the bytecode? How did you overcome the asynchronous loading behavior?
I am generating the bytecode manually (I plan on optmising it and releasing it as it's own framework) using the bytecode instructions. Unfortunately, I didn't overcome the asynchronous loading behavior (I did briefly investigate using ApplicationDomain.domainMemory), so I try to alleviate it from most developers in the unit testing framework integration classes.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
In the rather static world of Java, it is nice, but can't AS, as a dynamic language, be utilized in other ways, which makes this need less? I say this, as a question, as I am by no means an AS expert. I just think about Ruby, which is so dynamic, that requests like this disappear, as the language itself makes it possible to do it in other ways. Ways more native to that language.
?