flexRpcStubs 0.3 released

Brian LeGros | April 10th, 2010 | news, programming  

I’ve pushed out a couple new features and a bug fix for the flexRpcStubs project. Release 0.3 of the library is available for download on github. In terms of features, you can now filter HTTP calls by HTTP method as well as parameters and HTTP headers. Arguments provided to the result/fault method of HTTPServiceStub and RemoteObjectStub can now be of type literal, Class, Date, RegExp, or Hamcrest matcher rather than just literals and Hamcrest matchers; thanks to Drew for letting me steal the idea from Mockolate, my new favorite mock object framework for AS3. I also fixed a bug in RemoteObjectStub which was not dispatching events on the RemoteOperationStub; thanks to ropp for the fix.

If anyone has SDK classes they’d like to see stubs for, please let me know. I’ve been sent a draft of a ModuleLoaderStub and have a couple ideas for handling SWFLoader, so keep an eye out for more updates as I find time.

flexRpcStubs version 0.2 released

Brian LeGros | March 1st, 2010 | programming  

I burned the midnight oil tonight and was able to get release 0.2 of the flexRpcStubs project out on Github. In this release I’ve added the ability to add a rootCause to a fault on RemoteObjectStub, the ability to match parameters and headers on HTTPServiceStub, and the ability to use hamcrest-as3 matchers along with literals for both HTTPServiceStub and RemoteObjectStub. Additionally, I’ve thrown up some concise examples which, I hope, will help to show simple use cases using stubs in your unit tests. Here are links to the examples for HTTPServiceStub and RemoteObjectStub, just in case you don’t want to dig in. As always I look forward to feedback on the project. If you don’t want to build it yourself, I’ve thrown out a SWC for download on GitHub as well.

Stubs for mx.rpc classes moved to GitHub

Brian LeGros | February 9th, 2010 | programming  

A short note. For simplicities sake, I’ve created a project on GitHub (http://github.com/blegros/flexRpcStubs) to house the mx.rpc stubs which help with testing Flex service classes. The HTTPServiceStub and RemoteObjectStub classes have had a couple of bugs fixes and I’ve added build script to generate a SWC for simplicity. You can read more about the stubs in my initial posting; I hope to update the project soon with some practical usage examples.

If you have any suggestions for changes, feel free to fork and pass a pull request my way. Hope this helps those who asked for a bit more structure.

Experiences from using Grails to migrate legacy web services

Brian LeGros | August 15th, 2009 | programming  

Recently I was tasked with migrating a version of PHP web services to our new and growing Java stack. I had to maintain the current XML interface over HTTP but use our Java services behind the scenes to satisfy the requests. In an effort to make things easier on myself, I decided to use Grails because we were using a fairly typical Java stack of technologies (Spring, Hibernate, JUnit, etc.) that integrate extremely well with Grails’ underlying architecture. Basically, I had everything I needed in terms of persistence, domain, validation, etc in our existing stack, I just had to use Grails to expose it with a specific XML interface. Now that the proof of concept phase is complete for the project, I figured I’d share some of the niceties and challenges I encountered while approaching this problem with Grails.

First off, the expressiveness of Groovy as a language (I used 1.6.3) made the biggest task of mapping a very rigid, existing XML interface to our internal domain much easier and shorter than if I would have written it in Java. The messages were basically a flattening/expanding of our internal object graph with a couple of really quirky translations of data. Being able to treat literals as objects and the use of collection operators (*.) and methods (collect, each, find) saved me quite few lines of code. On the testing side of things, strictly from the perspective of Groovy, the as operator just kicks ass. Since mapping these interfaces was my focus, I didn’t find the need to mock too many expectations from our services, so stubbing out dependencies using maps and closures made testing SO much easier. I did find a down side to stubbing directly in Groovy however. If you are attempting to stub out a class without a no-arg constructor, you’re going to have to use a library like GMock which will allow you to create a stub passing constructor arguments. I’m not too partial to play, rewind type mocking frameworks, so I was a little bummed to have to pull in another resource to test, but it got the job done.

On the Grails side of things, integration with our existing services couldn’t have been easier. We used Grails 1.1.1 for our implementation. With a few tweaks to resources.xml, I was able to reuse our entire set of bean declarations and have them injected by name into our controllers. Although, we didn’t place our Hibernate mapping files in the /hibernate folder, I was able to create my own session factory with relative ease to get Hibernate up and running. Once I had this working, I knew I could eventually take advantage of the real reason I wanted to use Grails, RESTful interfaces over HTTP. The former PHP interface implemented a Front-Page Controller for all of its services, so using URLMappings I was able to emulate index.php to the rest of the world pretty easily. On a side note, the former interface was expecting a request variable name action which Grails reserves, but using a simple closure in UrlMappings allowed me to map it as a new request variable name and go on my way. In any case, since this is a specific endpoint, I am left open to create new controllers using Grails’ REST support as we migrate away from the older interfaces. This also means, from what I understand (thanks implicit GORM support from setting up Hibernate), I can start to use xml property binding to create my domain objects and the encodeAsXml() and encodeAsJson methods to serialize those object across the wire. This is pretty huge for us and has definitely improved my opinion of working with Java as a langauge on the web. It also solidified for me that Grails has matured quite a bit since I last gave it a test run. Lastly, I have to say that I’m really digging the simplicity of functional-testing plugin. Initially I had looked at WebTest, but this plugin is much less intimidating and was dirt simple to get started with.

Let me just stop hear and say that there were quite a few challenges to bringing Grails into our stack as well. The challenges weren’t particularly due to the implementation code itself but more on the supplemental side of things in terms of testing, continuous integration, and IDE support. Coming from an existing Java code base, we already had a set of testing tools we liked based on JUnit 4, JMock, and Spring testing. This included some really productive tools we built around dbunit supporting yaml for integration testing. Currently, Grails only supports JUnit 3, so ideally test cases should extend GroovyTestCase; there is also GrailsUnitTestCase which helps out with the mocking story quite a bit on unit tests for domain classes. Having to use JUnit 3 meant making a decision to go back and refactor testing support to work with both JUnit 4 and JUnit 3 or just rely more heavily on the functional testing support in Grails. Since I was just building controllers and a couple of domain classes, I chose to rely more on functional testing. For what its worth, there is a ticket out there for JUnit4 support. JUnit4 support aside, probably one of the most detracting things from testing with Grails is the tedium involved when testing controllers. Grails will inject a MockHttpServletRequest, MockHttpServletResponse, and MockHttpSession object for use by your controllers in the test case so you check redirected/forwarded urls, what responses will look like, easily create request edge cases, etc. By executing grails test-app -integration you can quickly see what I’m talking about. The injection process is painfully slow, at least on my last gen Macbook Pro, and I feel like I am always sitting, waiting around for these tests. I could have probably thinned down my controllers a bit, but most of the cost of executing these tests was on startup of the overall run. The only other thing I would have like to seen supported is the ability to have multiple test classes for the same domain, service, controller class. The permutations of response messages from each controller action were quite numerous, so I would have liked to create a test case per controller action rather than just one for the entire controller for readability. I only had luck getting a single test case to execute following the Grails naming conventions per object.

On the CI side of things, we build our Java projects using Maven. For large Java applications, we’ve been unable to find a simpler approach to build our software than using multi-module projects in Maven. Grails’ Maven support unfortunately did not work well with our multi-module project although it looks like they’ve made some updates for a quick fix that may help. Until then, I have our project POM hacked together a bit marking the executions of the plugin myself and using the dependency plugin to create a lib directory so I can work with grails outside of Maven w/o issues. Here are some of the relevant changes I made for my POM:

...
   <build>
      <plugins>
         <plugin>
            <groupId>org.grails</groupId>
            <artifactId>grails-maven-plugin</artifactId>
            <version>1.1-SNAPSHOT</version>
            <extensions>true</extensions>
            <executions>
               <execution>
                  <id>grails-clean</id>
                  <goals>
                     <goal>clean</goal>
                  </goals>
                  <phase>clean</phase>
               </execution>
               <execution>
                  <id>grails-test-app</id>
                  <goals>
                     <goal>test-app</goal>
                  </goals>
                  <phase>test</phase>
               </execution>
               <execution>
                  <id>grails-war</id>
                  <goals>
                     <goal>war</goal>
                  </goals>
                  <phase>package</phase>
               </execution>
            </executions>
            <dependencies>
               <dependency>
                  <groupId>org.codehaus.groovy</groupId>
                  <artifactId>groovy</artifactId>
                  <version>1.6.3</version>
               </dependency>
            </dependencies>
         </plugin>
         <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
               <execution>
                  <id>copy-grails-test-results-for-ci</id>
                  <phase>test</phase>
                  <configuration>
                     <tasks>
                        <property name="report.loc" location="${project.build.directory}/surefire-reports" />
                        <mkdir dir="${report.loc}" />
                        <copy todir="${report.loc}">
                           <fileset dir="${project.build.directory}/../test/reports">
                              <include name="**/TEST-*.xml" />
                              <exclude name="TEST-TestSuites.xml" />
                           </fileset>
                        </copy>
                     </tasks>
                  </configuration>
                  <goals>
                     <goal>run</goal>
                  </goals>
               </execution>
               <execution>
                  <id>copy-grails-war-to-target-for-lifecycle</id>
                  <phase>package</phase>
                  <configuration>
                     <tasks>
                        <copy todir="${project.build.directory}">
                           <fileset dir="${project.build.directory}/..">
                              <include name="*.war" />
                           </fileset>
                        </copy>
                     </tasks>
                  </configuration>
                  <goals>
                     <goal>run</goal>
                  </goals>
               </execution>
            </executions>
         </plugin>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-clean-plugin</artifactId>
            <version>2.3</version>
            <executions>
               <execution>
                  <id>clean-dependencies-in-lib-for-clean</id>
                  <phase>pre-clean</phase>
                  <goals>
                     <goal>clean</goal>
                  </goals>
                  <configuration>
                     <filesets>
                        <fileset>
                           <directory>${basedir}/lib</directory>
                           <includes>
                              <include>**/*.jar</include>
                           </includes>
                        </fileset>
                     </filesets>
                  </configuration>
               </execution>
            </executions>
         </plugin>
         <plugin>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
               <execution>
                  <id>copy-dependencies-into-lib</id>
                  <phase>generate-sources</phase>
                  <goals>
                     <goal>copy-dependencies</goal>
                  </goals>
                  <configuration>
                     <outputDirectory>${basedir}/lib</outputDirectory>
                     <excludeGroupIds>org.grails,org.codehaus.groovy</excludeGroupIds>
                  </configuration>
               </execution>
            </executions>
         </plugin>
         ...
      </plugins>
   </build>
...

In terms of CI server integration, we use Hudson and the only hiccup I ran into was to make sure the user the app server is running as has rights to create a /.grails folder in its home directory. It would be nice to be able to run the unit and integration tests with lifecycle rather than all at once (GRAILS-4569) as well as have a goal to just run functional tests, but I know they are working on it. It looks like a lot of work has been done with this plugin since the 1.1-SNAPSHOT we were using, so I’m eager to give it another try as we add more web services to the mix.

The last hurdle I had to overcome in using Groovy and Grails was IDE support. Working with Java and Flex, I’ve come to find that Eclipse is my staple development tool. Unfortunately, working with the combination of Maven, Groovy, and Grails, I found myself continually frustrated with the Groovy Eclipse plugin. m2eclipse really didn’t help either since a Grails application directory structure doesn’t match the traditional Maven project layout. In the end I found myself just using the code highlighting and limited refactoring support in Groovy Eclipse and then command-line for everything Grails related. I’d heard a lot about Netbeans 6.7 support for Grails, so I was optimistic, but the experience was more of a honeymoon than anything else. Netbeans has great Maven support, but unfortunately Grails projects in Netbeans can’t take advantage of that support; Netbeans doesn’t seem to have project nature support like Eclipse. I found myself using the built-in context menus for the Grails commands to keep me from going out to the command line, but this was pretty much the only feature I could justify using in Netbeans. The code completion support was far from responsive. I found myself waiting almost 30s in some cases for the “Please wait…” pop-up to only find that the Groovy editor couldn’t tell me anything about my typed/untyped variable other than the defaults. The Subversion client is terrible. When committing, each file in the changeset has a drop-down box for what you’d like to do with the file (exclude, add, commit) and the default is add or commit. If you have a working copy with lots of files you want to exclude (e.g. – artifacts from a build process) you have to one-by-one exclude each file. Also, I’m not sure why but the patch command is not available from the SVN context-menu on the project or files view. I had to hunt through the Team menu under Subversion to finally find the option. Subversive and Subclipse for Eclipse are a reason enough to avoid Netbeans if you’re an SVN user, IMO. Maybe I couldn’t find the hotkey or context menu option, but there also didn’t seem to be the ability to refresh a folder on the project or files view? I noticed that Netbeans was constantly “scanning folders” to find changes on the file system but I had no manual way of just saying refresh. Consequently I had a lot of folder sync’ing issues between the file system and Netbeans in the rare case I needed to use the command line. In the end, a lot of the day-to-day niceties I had become accustom to just weren’t in Netbeans and for the way I was using it, it just wasn’t for me. I had heard really good things about the IntelliJ support for Grails, but due to a license fee, my poor experience with Netbeans, and the fact that Eclipse does everything else I need for development, I ended up just sticking with Eclipse. On a more positive note, SpringSource has released an alpha build of a new Groovy plugin for Eclipse which has some great promise. There is also talk of STS for Eclipse adding Grails support, so I’m hopeful the Groovy/Grails IDE experience can only improve from here.

Overall, despite the challenges I encountered, Grails is a dynamite web framework if you’re already working with Spring and Hibernate in your stack. I’ll acknowledge I may be a complete n00b in terms of the learning curve still, so if I’ve totally missed the boat on some of my points, please let me know so I can update this post. I’m excited to continue using Grails, especially with the newer releases coming up which will including Spring 3.0 and Spring-Flex.

Custom test runner for FlexUnit 4 and mock-as3

Brian LeGros | August 6th, 2009 | programming  

At the August Adogo meeting, I presented on the new features coming in FlexUnit4 and mock-as3. During the presentation, I showed the following sample (which has been updated) of how a FlexUnit 4 test using a customer runner for mock-as3 would work. Please keep in mind the example is a bit contrive but exemplifies the configuration options for the runner.

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
package us.adogo.mock
{
   import com.anywebcam.mock.Mockery;
 
   import org.flexunit.Assert;
   import org.flexunit.assertThat;
   import org.hamcrest.core.not;
   import org.hamcrest.object.equalTo;
   import org.hamcrest.object.hasProperty;
   import org.hamcrest.object.nullValue;
 
   [RunWith("com.anywebcam.mock.runner.MockAs3TestRunner")]
   public class ExampleUsingMockAs3Runner {
      public var mockery : Mockery;
 
      [Mock]
      public var user : User;
 
      [Mock(inject="false")]
      public var account : Account;
 
      private var controller : UserController; 
 
      [Before]
      public function setUp() : void {
         account = mockery.nice(Account, ["1234567890"]) as Account;
         this.controller = new UserController(); 
      }
 
      [Test]
      public function testAddUserWithUsernameAndPasswordOnly() : void {
         //setup mock
         mockery.mock(user).method("save").calls(function () : void {
            user.id = 1;
         }).once;
 
         //populate object properties as you usually would
         user.username = "bobdobbs";
         user.password = "mysecret";
 
         //execute my controller method being tested
         controller.addUser(user);
 
         //test that the currentUsers went up by one and that its id is 1
         assertThat(controller.currentUsers.length, equalTo(1));
         assertThat(controller.currentUsers.getItemAt(0).id, equalTo(1));
         assertThat(controller.currentUsers.getItemAt(0).username, equalTo("bobdobbs"));
         assertThat(controller.currentUsers.getItemAt(0).password, equalTo("mysecret"));
      }
 
      [Test]
      public function testValidateUserAccount() : void {
         //setup mock
         mockery.mock(account).method("isValid").withArgs(User).returns(true).once;
 
         //execute my controller method being tested
         var expected : Boolean = controller.validateUser(new User(), account);
 
         //test that the account is valid
         Assert.assertTrue(expected);
      }
 
      [Test(verify="false")]
      public function testDefaultUserSize() : void {
         assertThat(controller, hasProperty("currentUsers"));
         assertThat(controller.currentUsers, not(nullValue()));
         assertThat(controller.currentUsers.length, equalTo(0));
      }
   }
}

This evening I updated the Adogo SVN with a working version of this runner and thought I’d detail its initial implementation.

  • To use the runner place the metadata [RunWith("com.anywebcam.mock.runner.MockAs3TestRunner")] on the test class’ declaration.
  • Once annotated with metadata, the test class will not run unless a public variable, or setter, is available of type com.anywebcam.mock.Mockery. This will allow the runner to inject a Mockery object and prepare() the mockery to produce mock objects. Using and preparing a mockery is a new requirement for mock-as3 if you’re utilizing type-safe mocks due to the asmock integration.
  • Any public variable, or setter, annotated with the metadata [Mock] will have a type safe version of it injected automatically prior to the execution of each test method. By default a nice mock will be used, but if you specify the “type” attribute (e.g. – [Mock(type="strict")]) as “strict” then a strict mock will then be used. The difference between nice and strict mocks, is the equivalent of the “ignoreMissing” constructor argument on com.anywebcam.mock.Mock being set to true, or false, respectively.
  • If the [Mock] metadata is used on a class with a constructor requiring arguments, whether they are optional or …rest, the runner cannot automatically inject the mock due to a limitation in its underlying dependency on the asmock library. If you need a mock object instance using constructor arguments, set the attribute “inject” as false (e.g. – [Mock(inject="false")]. Doing so will tell the runner that you’re going to use the mock-as3 framework directly to create the mock. The ideal place to do this in your test is within the [Before] methods defined for the test class. By going through all this hoopla, you get automatic mock prepration and verification, just as with the automatically injected mock objects.
  • After the execution of each test method, the runner will automatically call verify() on each object variable/property marked with the [Mock] metadata. If you’d like to disable verify() from being called on all mocks for a test method, simply add the attribute “verify”, with a value of false, to your [Test] metadata (e.g. – [Test(verify="false")]). This can be helpful if you want the runner to inject the mockery and all of your mocks, but you would like to specify which mocks are verified for the test.
  • Currently this runner requires FlexUnit 4 Beta 2 and a custom build of mock-as3 to work as stated. You can find SWCs for each in the Adogo August project linked above.

What’s important to note, is that this runner is more restrictive than using the mock-as3 framework directly, so it may not suite your needs. I based this runner off of concepts I saw in the JMock runner for JUnit 4, the @Mock annotation found in Mockito for Java, and my own testing practices. If anyone finds time to play with it and you find any gremlins, let me know. In speaking with Drew, it looks like this may make it into the next release of mock-as3 along with a couple of other cool features, so I hope people find it helpful.

UPDATE: Drew has been kind enough to deploy this FU4 test runner to the mock-as3 SVN repository under the class name com.anywebcam.mock.runner.MockRunner. Please use this copy for future reference. Please also note this version will only work with FlexUnit4 Beta2 and earlier. FlexUnit4 RC1 has changed the implementation for test runners and this code will need to be updated.

Adogo Meeting Monday

Brian LeGros | July 28th, 2009 | news  

I’m going to be presenting at this Monday’s (08/03/2009) August Adogo meeting after a few month hiatus due to the new baby. I’ll be running through the new features in FlexUnit4 and some work I’m doing with Drew on mock-as3. Afterwards, we are going to have a group discussion on “Uses for RIAs” where we’ll try to talk about where RIAs are applicable and how they can be abused. If you’re interested in testing in Flex or developer discussion, come by and see the latest and greatest coming out of the community. We have a sponsor for the meeting, so there will be food.

I hope you can make it out.

FlexUnit 4 public alpha now available

Brian LeGros | May 4th, 2009 | news  

Just a quick note. Last night the Mike did a blog post on the new features coming to FlexUnit and the working title of FlexUnit 4 for the project. You can find the blog post @ http://blogs.digitalprimates.net/codeSlinger/index.cfm/2009/5/3/FlexUnit-4-in-360-seconds. There is a link to the alpha in the post and as soon as it’s available on Adobe’s servers, we’ll publish that URL as well. Keep in mind that it does support legacy FlexUnit and Fluint tests, so dig in and let us know what you think!

Fluint 1.1.1 ready to be released

Brian LeGros | April 29th, 2009 | programming  

After a busy couple of months, I’ve finally gotten off of my ass and decided to do the integration for Fluint 1.1.1 and created the new branch for Fluint 1.2. We’re in the process of getting the artifacts into Google Code, but the release tag is in Subversion now for those who are interested. In this release we had a couple small fixes:

  • Issue #34 – Assets are not located in net/digitalprimates/fluint folder
  • Issue #35 – Can’t find TestResponder
  • Issue #37 – Air Test Runner needs better error handling
  • Added new target to ant build for building the airtestrunner as an .airi file (target => “airtestrunner-intermediary”)
  • Added flex builder metadata files to the samples project

I’ve already added my stubs for HTTPService and RemoteObject to the 1.2 branch for those who want to play around with them. Matt Hughes’ sequence improvements will also be included in 1.2 once I figure out where they ended up. For 1.2 I’m hoping to add some documentation for the wiki to address some really early issues as well as #28. In terms of bug fixes and new features, I’m hoping to tackle issues 32, 38, 41, 42, 44, 45, and 46 (#44 being the highest priority).

With a new baby coming in May, I’m unsure of my schedule, but I’m gonna do my best to get what I can ready (=who knows). In the mean time, kick the tires for us and let us know if there are any issues with 1.1.1.

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.

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.