Archive for February, 2009

Using Stubs for HTTPService and RemoteObject in Flex

Brian LeGros | February 21st, 2009 | programming  

UPDATE: I’ve finally created a project for these stubs on GitHub @ http://github.com/blegros/flexRpcStubs. Please consider this code more up to date then what follows below.

Recently I’ve been working on producing stub versions of HTTPService and RemoteObject for some integration tests I’m writing. If you haven’t worked with the concept of a stub before, think of a stub a re-implementation of an object created to produced canned answers to calls made to it during a test (borrowed from Mocks aren’t Stubs). A stub is intended to be purposefully ignorant to everything but what is being asked of it in a test. If you’re familiar with the concept of a mock object, a stub can be considered a specialization of a mock object that has no expectations to manage rather only return values. In a lot of circles the term mock and stub are used interchangeably, but I think the difference is important to note. If you’re interested more in the subject, come and see my presentation at FlexCamp Miami on March 6th, 2009.

Typically, in the case of integration testing, stubs are used to impersonate objects which have direct contact with resources external to your application, or component. In the context of most programming langauges I’ve worked with, these are typically classes built into extension libraries for the language (e.g. – JDBC, log4j, javax.mail, etc). In the context of Flex, services are the primary sets of classes that we end up wishing we could stub out. Unfortunately, the need to stub these types of classes can introduce some complexities, especially without a good mock object framework, because very rarely are the interfaces to these classes simple enough for a developer to impersonate on the fly (i.e. – inline in a single test). In the Java world, frameworks like Spring will create helper objects for testing to supplement these needs (e.g. – MockHttpSession and AbstractTransactionalJUnit4SpringContextTests).

In Flex, I haven’t really found a great set implementations for stubs yet, so this weekend I threw together a first draft of working stub classes for HTTPService and RemoteObject in AS3 using the Flex SDK 3.2. I’ve been using variants of these for my testing and so far, so good. Anyone interested in stubbing WebService, can proabably create a similar model to what I’ve done in RemoteObjectStub and be successful. Each stub is type-safe and adds the following properties for configuration to their base classes:

  • delay : Number – Default: 1000 – Number of milliseconds to delay calling registered responders and dispatch the appropriate events for the stub.
  • result : Function – The method used to map result, or fault, objects to specific signatures of send (for HTTPService) or a method name (for RemoteObject). If passed an object deriving from type mx.rpc.Fault, a mx.rpc.events.FaultEvent will be dispatched rather than a mx.rpc.events.ResultEvent.
  • fault : Function – A sugar method that will take in a faultCode, faultString, and faultDetail to create a mx.rpc.Fault and delegate to result().

Each class also supports the dispatching of the appropriate events to mx.rpc.AsyncToken objects as well as the stub instances themselves, just like their base classes. Here is a example of using HTTPServiceStub, in place of HTTPService, in a test case written using fluint:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ClassWithHTTPServiceDependencyTest extends TestCase
{
   private var _classBeingTested : ClassWithHTTPServiceDependency;
   private var _stub : HTTPServiceStub;
 
   override protected function setUp() : void
   {
      _classBeingTested = new ClassWithHTTPServiceDependency();
      _stub = new HTTPServiceStub("http://thisisntarealdomain.com");
      _stub.delay = 500;
      _classBeingTested.service = _stub;
   }
 
   public function testSendWithTokenResult() : void
   {
      var result : Function = function (event : DynamicEvent, passThroughData : Object) : void
      {
         assertEquals("GOAL!", event.payload);
      };
 
      _stub.result(null, "GOAL!");
 
      _classBeingTested.addEventListener("success", asyncHandler(result, 2000));
      _classBeingTested.someMethodUsingHTTPService();
   }
}

In the above example, the behavior being tested is that when a ResultEvent is dispatched to ClassWithHTTPServiceDependency, it dispatches its own mx.events.DynamicEvent with a reference to the result property of the ResultEvent in its payload property. Please note, the call to send() is being made inside of someMethodUsingHTTPService() but could really be made anywhere inside of the object, we don’t care since the stub can impersonate HTTPService.

The implementations for the HTTPServiceStub and RemoteObjectStub are something I hope to get feedback on and maybe get included in fluint or mock-as3, although I’m sure Drew can think of sexier implementations. Below is the source for anyone who is interested in giving them a go to see if they can help your testing process. I have a few unit tests for each as well, so if anyone’s interested, just let me know and I’ll post that code as well.

Happy coding and here’s to these not working and failing you miserably …

HTTPServiceStub.as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package net.digitalprimates.fluint.stubs
{
   import flash.events.TimerEvent;
   import flash.utils.Dictionary;
   import flash.utils.Timer;
 
   import mx.rpc.AsyncToken;
   import mx.rpc.Fault;
   import mx.rpc.IResponder;
   import mx.rpc.events.AbstractEvent;
   import mx.rpc.events.FaultEvent;
   import mx.rpc.events.ResultEvent;
   import mx.rpc.http.HTTPService;
 
   /**
    * Idea borrowed from Sonke Rohde @ http://soenkerohde.com/2008/10/conditional-compilation-to-mock-with-swiz/
    **/
   public class HTTPServiceStub extends HTTPService
   {
      private var _resultData : Dictionary;
 
      //default num of milliseconds to wait before dispatching events
      //don't put too low otherwise your token responders may not be registered
      public var delay : Number = 1000;
 
      private var token : AsyncToken;
      private var parameters : Object;
 
      public function HTTPServiceStub(rootURL : String = null, destination : String = null)
      {
         super(rootURL, destination);
         _resultData = new Dictionary();
      }
 
      public function result(parameters : Object, data : *) : void
      {
         _resultData[parameters] = data;
      }
 
      public function fault(parameters : Object, code : String, string : String, detail : String) : void
      {
         var fault : Fault = new Fault(code, string, detail);
         this.result(parameters, fault);
      }
 
      override public function send(parameters : Object = null) : AsyncToken
      {
         return configureResponseTimer(parameters);
      }
 
      private function configureResponseTimer(parameters : Object) : AsyncToken
      {
         token = new AsyncToken(null);
         this.parameters = parameters;
 
         //use a time to give time for the caller to map responders to the asyncToken
         var timer : Timer = new Timer(this.delay, 1);
         timer.addEventListener(TimerEvent.TIMER_COMPLETE, handleTimer);
 
         timer.start();
 
         return token;
      }
 
      private function handleTimer(event : TimerEvent) : void
      {
         //clean-up
         event.target.removeEventListener(TimerEvent.TIMER_COMPLETE, handleTimer);
 
         //loop over all responders to emulate a successful call being made
         for each(var responder : IResponder in token.responders)
         {
           var response : Function = isFaultCall(parameters) ? responder.fault : responder.result;
           response.apply(null, [generateEvent(parameters)]);
         }
 
         //dispatch event to service just in case token wasn't used
         dispatchEvent(generateEvent(parameters));
      }
 
      private function isFaultCall(parameters : Object) : Boolean
      {
         return (_resultData[parameters] is Fault);
      }
 
      private function generateEvent(parameters : Object) : AbstractEvent
      {
         if(isFaultCall(parameters))
         {
            return new FaultEvent(FaultEvent.FAULT, false, true, _resultData[parameters]);
         }
         else
         {
            return new ResultEvent(ResultEvent.RESULT, false, true, _resultData[parameters]);
         }
      }
   }
}

RemoteObjectStub.as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package net.digitalprimates.fluint.stubs
{
   import flash.utils.Dictionary;
 
   import mx.rpc.AbstractOperation;
   import mx.rpc.Fault;
   import mx.rpc.remoting.RemoteObject;
 
   public dynamic class RemoteObjectStub extends RemoteObject
   {
      private var _resultData : Dictionary;
 
      //default num of milliseconds to wait before dispatching events
      //don't put too low otherwise your token responders may not be registered
      public var delay : Number = 1000;
 
      public function RemoteObjectStub(destination : String = null)
      {
         super(destination);
         _resultData = new Dictionary();
      }
 
      public function result(methodName : String, args : Array,  data : *) : void
      {
         if(!methodName || methodName.length == 0)
         {
            throw new Error("Cannot use null or empty method names in RemoteObjectStub.");
         }
 
         if(!args)
         {
            args = [];
         }
 
         if(!_resultData[methodName])
         {
            _resultData[methodName] = new Dictionary();
         }
 
         _resultData[methodName][args.toString()] = data;
      }
 
      public function fault(methodName : String, args : Array, code : String, string : String, detail : String) : void
      {
         var fault : Fault = new Fault(code, string, detail);
         this.result(methodName, args, fault);
      }
 
      override public function getOperation(name : String) : AbstractOperation
      {
         return new OperationStub(this, name, _resultData[name]);
      }
   }
}
 
import mx.rpc.AsyncToken;
import net.digitalprimates.fluint.stubs.RemoteObjectStub;
import mx.messaging.config.ServerConfig;
import mx.rpc.Fault;
import flash.utils.Dictionary;
import mx.rpc.remoting.Operation;
import mx.rpc.remoting.RemoteObject;
import flash.events.TimerEvent;
import mx.rpc.events.AbstractEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import flash.utils.Timer;
import mx.rpc.IResponder;
 
internal class OperationStub extends Operation
{
   public var _resultData : Dictionary;
 
   private var token : AsyncToken;
   private var args : Array;
 
   public function OperationStub(remoteObject : RemoteObject, name : String, resultData : Dictionary)
   {
      super(remoteObject, name);
      _resultData = resultData;
   }
 
   override public function send(... args:Array) : AsyncToken
   {
      return configureResponseTimer(args);
   }
 
   private function configureResponseTimer(args : Array) : AsyncToken
   {
      token = new AsyncToken(null);
      this.args = args;
 
      var stub : RemoteObjectStub = RemoteObjectStub(service);
 
      //use a time to give time for the caller to map responders to the asyncToken
      var timer : Timer = new Timer(stub.delay, 1);
      timer.addEventListener(TimerEvent.TIMER_COMPLETE, handleTimer);
 
      timer.start();
 
      return token;
   }
 
   private function handleTimer(event : TimerEvent) : void
   {
      //loop over all responders to emulate a successful call being made
      for each(var responder : IResponder in token.responders)
      {
         var response : Function = isFault(args) ? responder.fault : responder.result;
         response.apply(null, [generateEvent(args)]);
      }
 
      //send the result event to the RemoteObject as well
      service.dispatchEvent(generateEvent(args));
   }
 
   private function isFault(args : Array) : Boolean
   {
      return (_resultData[args.toString()] is Fault);
   }
 
   private function generateEvent(args : Array) : AbstractEvent
   {
      if(isFault(args))
      {
         return new FaultEvent(FaultEvent.FAULT, false, true, _resultData[args.toString()]);
      }
      else
      {
         var result : * = _resultData[args.toString()];
         return new ResultEvent(ResultEvent.RESULT, false, true, _resultData[args.toString()]);
      }
   }
}

UPDATE: – Thanks to Joel Hooks for exercising these a bit more and figuring out a GC issue with the timer that provided some flukely behavior. The above code now supports his fixes in both HTTPServiceStub and RemoteObjectStub. I’ve also run my unit tests against the latest Flex 4 beta and everything passed. Looks like these may be more useful as we start to use Flex 4.

Banzai Sushi and Thai Restaurant – Melbourne, FL

Brian LeGros | February 20th, 2009 | food  

Today my wife and I walked away from dinner saying, “That was the best meal we’ve had in a long time.” We’ve been going to Banzai Sushi and Thai Restaurant since the first week we moved into Brevard county. A friend of mine, who had grown up in Melbourne, recommended it as one of his favorite Japanese and Thai restaurants in the area. Ever since that first visit we have never had a bad experience being spoiled with amazing food, service, and ambiance. To call Banzai just an eatery doesn’t do it justice, it’s a dining experience. In fact Banzai has kept up its dining traditions when most restaurateurs have been quick to abandon them in these tough economic times. Allow me to share our latest experience.

Upon arriving at their small, but comfortable location, you are immediately greeted by one of the servers and immediately seated at a table; we’ve never had to wait. Within a minute or so, your server arrives with hot towels to clean your hands and small salad bowls with tiny spoons. The salad is a mixture of diced tomatoes and cucumber, shredded carrots, mandarin oranges, and pieces of fried tempura batter. It’s a great balance of sweet and salty and perfect to wet your appetite. We usually like to start with a pot of hot green tea ($1.50 p/person) and a couple ice waters. Once we work our way through the massive list of sushi, Japanese, Thai, and Chinese selections, we’ve never had to wait more than a 10-15 minutes to start our meal. This time around, I ordered one of the most fragrant and tasty green curry dishes with chicken (~$10) and my wife ordered the small green salad (~$3) and the shrimp and vegetable tempura appetizer (~$8). The curry is prepared with coconut millk, bell pepper, bamboo shoots, fresh basil, and red pepper oil in a huge portion; if you like spicy, try the Thai hot for that good kind of spicy. The small green salad comes with your option of ginger or honey ginger dressing on top of iceberg and romaine lettuce, tomato, shredded carrots, and sliced cucumber. The tempura appetizer is big enough to be a meal for a single person coming with three shrimp, onion, broccoli, and sweet potato. If all of this food wasn’t enough, at the end of the meal, the server brings out fried sweet doughnuts, dusted with sugar granules. This time around we didn’t order any sushi, but I have to say that Banzai has some of the best quality sushi for your dollar. Their sushi chefs will make pretty much anything you’d like, even if it’s not on the menu. The rolls are packed full of ingredients and they even have surfboards for larger groups.

Adding to the experience of dining at Banzai, every server in the restaurant is your server. They are all equally friendly and courteous offering help to anyone who needs it. I’m not sure if he’s the manager or owner, but even the man in charge is out and about taking care of his customers, making it a pleasent evening for everyone in the restaurant. What’s probably the best part of the service however, is the facet most people don’t consider, invisibility. Banzai works like a well oiled machine providing its customers with whatever they need, but without them having to ask for it. This breeds the peaceful quiet that I have to believe all of Banzai’s patrons associate with the restaurant.

Banzai Sushi and Thai Restaurant provides a high quality experience for what has become a more reasonable price now-a-days. The quality of food is high and you’ll find yourself eager for your next visit before you even walk out of the door. If you’re looking for an establishment that is just as much about the time you spend there as it is the food, do yourself a favor and give Banzai and shot. I promise you won’t regret it.

Banzai Sushi and Thai Restaurant is located in the Publix plaza on the corner of Lake Washington and Wickham at 3208 Lake Washington Road, Melbourne, FL.


Banzai Sushi & Thai Restaurant on Urbanspoon


FlexCamp Miami – Discount Code – $10 off

Brian LeGros | February 20th, 2009 | news  

If there was anyone who was interested in going to FlexCamp Miami, but could use a lower price, look no further. Use the discount code Adogo, when registering for the conference and save $10 off the purchase price! The discount code is only good until next Friday, 02/27/2009, @ midnight. If you haven’t registered yet but were debating whether or not you should go, here’s your incentive; that’s a half a tank of gas … well as at least in my Civic.

Only two weeks remaining until the event, 03/06/2009, so don’t miss out!

Testing Tools for Flex

Brian LeGros | February 20th, 2009 | programming  

Over the last few weeks, while preparing my presentation for FlexCamp Miami a bit more, I have run into quite the plethora of tools to help developers test their Flex applications. I thought I’d take some time to break down what types of tools are available to help us out.

Unit Testing Frameworks

Mock Object Frameworks

Other useful libraries

My favorite combination right now is fluint, mock-as3, hamcrest-as3, and asx. Although asmock supports type-safe mocking, I’m not a big fan of the record/replay model; for now I’m gonna stick with mock-as3 and wait until it supports type-safe mocks (<cough>loom integration<cough>. I’ve also found a major need for stubs when classes in my integration tests depend on HTTPService and RemoteObject. Sometimes its tough to mock out these classes because of complex interfaces these objects maintain, so I’ve come up with a pretty cool way of handling this. Hopefully I can publish the sample code here in the next few days for those who are interested.

I’d also like to give props to a couple people I’ve been working with in the community: Michael Labriola, Max Porges and Drew Bourne. Mike is working tirelessly to bring a more modern unit testing framework, a la JUnit 4 + more, to Flex via fluint along with tons of other initiatives. mock-as3, hamcrest-as3, and asx are all APIs that Drew has written; hamcrest-as3 alone is going to be providing some really powerful integration for assertions in unit testing frameworks. Max has been chipping away at the ABC spec with loom for months now, and once finished it’s going to provide a lot of great potential for more mature tooling/libraries in the Flex world … think AOP. Each of these code-bases are intuitive, easy to use, and were some of the biggest pieces missing from the Flex testing world until now. Keep up the great work guys! With any luck, we may soon have the tools we need to be as productive as developers in almost every other language out there.

Fluint 1.1.0 Released!

Brian LeGros | February 12th, 2009 | programming  

Over the last few months, I’ve been working with Michael Labriola on the next minor version release of Fluint. Well, this evening Michael and I put the finishing touches on release 1.1.0. This release brought all of the changes I made to get Fluint working at the office for our CI process to the community. There are a couple of cool little gems that I think will be helpful for some:

  • Separation of failures and errors in test reporting (visual and XML).
  • XML compliant output with most CI servers as well as the JUnitReport task in Ant and the Surefire Reporting plugin in Maven.
  • Support in the Ant task for truly headless executions of the AIR Test Runner via xvfb-run.
  • Support for relative paths in the Ant task and AIR Test Runner.
  • Better error handling for non-compliant modules loaded into the AIR Test Runner.
  • LogBook integration for debugging
  • A big folder re-organization and Ant scripts to support developers when they need specialty builds.
  • Agreed upon a branching and tagging strategy so the community can have stable snapshots of source to build.

We probably should put together a ChangeLog wiki page, so that may be available soon. We did a few updates to the wiki, so stop by the Google Code site and check out the changes. Also, provide feedback on the discussion list, if there are features you’d like to see implemented, bugs that you find, or just general questions you need help with. People are great about helping on the list, so don’t feel discouraged.

This is my first major release of an OSS project that I’ve had code go into, so thanks to everyone who helped with the release, especially my wife for putting up with a lot of late nights. There is a lot more to come with Fluint over the next year that is going to be really exciting. Look for announcements during my presentation at FlexCamp Miami and on the discussion mailing list. Hope you enjoy the new release!

Adogo presentation went long

Brian LeGros | February 10th, 2009 | news  

Last night I gave my 2nd pass at my “Continous Integration” presentation that I will be giving at FlexCamp Miami in March. The presentation went an hour and forty minutes … yeah, it was really long winded. People were receptive to the content, I just think it was a lot to process in one session. Dan had some great suggestions about shortening the amount of time spent on locking strategies in source control, which should give me a lot of time back. I know the conference was looking for a testing topic and I gave them a CI topic, so I’ll probably also focus more on testing and examples than the rest of the content. After listening to myself on the recording, I also think I need laugh a little less at my own jokes, and be a little more professional; I’ll be working on that in the coming weeks.

If anyone who was at the meeting, or wants to watch the recording, has some time, I’d really be up for feedback on the presentation content and my presentation style. Feel free to let ‘er rip and don’t hold any punches. I can take it :*(

Which assertions do you use in unit tests?

Brian LeGros | February 6th, 2009 | programming  

Over the last few days, I’ve decided to start putting some time into adding some new assertion functions into fluint. Here are a few that I was thinking of adding:

assertRegExp(expected : RegExp, actual : String) : void
assertArraysEqual(expected : Array, actual : Array, compare : Function) : void
assertDictionariesEqual(expected : Dictionary, actual  : Dictionary) : void

Along with each of these, their boolean counterpart would also be available (i.e. – assertArraysNotEqual). My question to other developers is, which assertions would you like to see available in your unit testing framework of choice? The way I figure, the more options available in the assertion stockpile, no matter how simplistic they may seem, the more explicit we can make our tests. Would anyone be interested in seeing some type of assertion using callbacks (i.e. assertReturnsEquals(actualCallback : Function, args ..rest, expectedResult)? Feels like a bit of a stretch, but it’s a datatype, so maybe it should be considered. Anything for events that may be useful?

What do you think?

Presenting at the Adogo this month

Brian LeGros | February 3rd, 2009 | news  

It seems like every month, the Adogo and RAnDOM meetings creep up on me. That being said, we’ve pushed the Adogo meeting for February back to February 9th this month instead of yesterday, February 2nd, to try and make up some time to prep. I will be presenting on the topic of “Continuous Integration” (primer) and afterward we’ll be having a round-table discussion about what everyone’s been up to and working on. Should be a pretty lay back meeting and I promise to put everyone to sleep, so bring your pillows. This will be a practice run for my the presentation that I will be giving at FlexCamp Miami on March 6th, so if you like what you see (with, or without. the sexual overtones), you should try to make it down for a day of fun with Flex.

See ya Monday, if you can make it; if not, thanks for sparing me the embarrassment that was inevitable. :’(