AsUnit (AS3): RemoteTestCase for testing asynchronous data using Flash Remoting

Posted on November 29, 2007

AsUnit is the first choice for Test-Driven Development using pure Flash applications. However, currently you find only one test case for asynchronous data using an instance of the flash.net.URLLoader class, but it seems neither for calling methods based on Flash Remoting using the flash.net.NetConnection class. So I decided to add a new test case called asunit.framework.RemotingTestCase" to the AsUnit Framework and hope it would be helpful for the community - check it out ;-)

Examples

Successfully asynchronous tests

Unsuccessfully asynchronous tests

Source

For more information check out the comments within the code, too ;-)

  1 package asunit.framework
  2 {
  3     import flash.errors.IllegalOperationError;
  4     import flash.events.IOErrorEvent;
  5     import flash.events.NetStatusEvent;
  6     import flash.events.SecurityErrorEvent;
  7     import flash.net.NetConnection;
  8     import flash.net.ObjectEncoding;
  9     import flash.net.Responder;
 10 
 11     import asunit.framework.TestCase;
 12     import asunit.util.ArrayIterator;
 13 
 14     /**
 15     * RemotingTestCase
 16     * @author  Jens Krause [www.websector.de]
 17     * @date    11/29/07
 18     *
 19     */
 20     public class RemotingTestCase extends TestCase
 21     {
 22 
 23         protected var connection: NetConnection;
 24         /**
 25        * Constructor
 26        * @param testMethod     String      Name of the test case
 27        *
 28        */
 29         public function RemotingTestCase(testMethod: String = null)
 30         {
 31             super(testMethod);
 32         }
 33 
 34         /**
 35        * Inits a netConnection instance and add all necessary event listeners
 36        *
 37        */
 38         protected function initConnection():void
 39         {
 40             if (connection == null)
 41             {
 42                 connection = new NetConnection();
 43 
 44                 connection.addEventListener(NetStatusEvent.NET_STATUS, connectionStatusHandler);
 45                 connection.addEventListener(IOErrorEvent.IO_ERROR, connectionIOErrorHandler);
 46                 connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR , connectionSecurityErrorHandler);
 47             }
 48         }
 49 
 50         /**
 51        * Dispose the netConnection instance
 52        *
 53        */
 54         protected function disposeConnection():void
 55         {
 56             if (connection != null)
 57             {
 58                 connection.removeEventListener(NetStatusEvent.NET_STATUS, connectionStatusHandler);
 59                 connection.removeEventListener(IOErrorEvent.IO_ERROR, connectionIOErrorHandler);
 60                 connection.removeEventListener(SecurityErrorEvent.SECURITY_ERROR , connectionSecurityErrorHandler);
 61 
 62                 connection = null;
 63             }
 64         }
 65 
 66         /**
 67        * Callback handler for receiving SecurityErrorEvent
 68        * @param event      SecurityErrorEvent
 69        *
 70        */
 71         protected function connectionSecurityErrorHandler(event: SecurityErrorEvent): void
 72         {
 73             result.addError(this, new IllegalOperationError(event.toString()));
 74             isComplete = true;
 75         }
 76 
 77         /**
 78        * Callback handler for receiving IOErrorEvent
 79        * @param event      IOErrorEvent
 80        *
 81        */
 82         protected function connectionIOErrorHandler(event: IOErrorEvent): void
 83         {
 84             result.addError(this, new IllegalOperationError(event.toString()));
 85             isComplete = true;
 86         }
 87 
 88         /**
 89        * Callback handler for receiving NetStatusEvent
 90        * @param event      NetStatusEvent
 91        *
 92        */
 93         protected function connectionStatusHandler(event: NetStatusEvent): void
 94         {
 95 
 96         }
 97 
 98         /**
 99        * Connects the gateway
100        *
101        * @param $gateway       String      Remote gateway
102        * @param $encoding      uint        Object encoding using either AMF0 or AMF3
103        *
104        */
105         protected function connect ($gateway: String = null, $encoding: uint = 0): void
106         {
107             initConnection();
108 
109             connection.objectEncoding = ($encoding > ObjectEncoding.AMF0) ? $encoding : ObjectEncoding.AMF0;
110 
111             try {
112                 connection.connect($gateway);
113             }
114             catch(error: Error)
115             {
116                 result.addError(this, error);
117             }
118         };
119 
120         /**
121        * Calls a remote service method and test it
122        *
123        * @param $method        String      Remote service
124        * @param $responder     Responder   Responder to handle remoting calls
125        * @param $arguments     Array       Rest paramaters (optional)
126        *
127        */
128         protected function call ($method: String = null, $responder: Responder = null, ...$arguments): void
129         {
130             var hasReferenceError: Boolean = false;
131 
132             // parameters for calling connection.call();
133             // To avoid using the type unsafe ...rest operator I decided to use type safe parameters within RemotingTestCase.call()
134             // and apply these later to connection.call();
135             var params: Array = [];
136 
137             // check remote method
138             if ($method != null)
139             {
140                 params.push($method);
141             }
142             else
143             {
144                 result.addError(this, new ReferenceError("RemotingTestCase.call() has to defined a remote method."));
145                 hasReferenceError = true;
146             }
147 
148             // check responder
149             if ($responder != null)
150             {
151                 params.push($responder);
152             }
153             else
154             {
155                 result.addError(this, new ReferenceError("RemotingTestCase.call() has to defined a responder to handling its results."));
156                 hasReferenceError = true;
157             }
158 
159             // In case of a reference error invoke test running instantly
160             // to show the errors created above and return
161             if (hasReferenceError)
162             {
163                 super.run();
164                 return;
165             }
166 
167 
168             var arrIterator: ArrayIterator = new ArrayIterator($arguments);
169             while (arrIterator.hasNext())
170             {
171                 params.push(arrIterator.next());
172             }
173 
174             // call remote service
175             try {
176                 connection.call.apply(null, params);
177             }
178             catch(error: Error)
179             {
180                 result.addError(this, error);
181             }
182 
183 
184         };
185     }
186 }

The following test example based on my Flash Remoting example using OpenAMF described at the previous article called "Best practices: Custom class mapping using OpenAMF and AS3 (Flash CS3 - not Flex)"

  1 package tests
  2 {
  3     import flash.net.ObjectEncoding;
  4     import flash.net.Responder;
  5 
  6     import asunit.framework.RemotingTestCase;
  7 
  8     /**
  9     * RemotingTestCaseExample
 10     * @author  Jens Krause [www.websector.de]
 11     * @date    11/29/97
 12     */
 13     public class RemotingTestCaseExample extends RemotingTestCase
 14     {
 15         private var _result: Object;
 16         private var _userVO: UserVO;
 17 
 18         /**
 19        * Constructor
 20        * @param testMethod     String      Name of the test case
 21        *
 22        */
 23         public function RemotingTestCaseExample(testMethod: String = null)
 24         {
 25             super(testMethod);
 26 
 27         }
 28 
 29         /**
 30        * After a test is executed the tearDown method is called
 31        * and removed all references to test objects
 32        *
 33        */
 34         override protected function tearDown():void
 35         {
 36             _userVO = null;
 37         }
 38 
 39         /**
 40        * Before a test is executed the setUp method is called
 41        * which instantiate all necessary test objects
 42        *
 43        */
 44 
 45          override protected function setUp(): void
 46          {
 47             _userVO = _result as UserVO;
 48          }
 49 
 50         /**
 51        * Runs the test
 52        *
 53        */
 54         public override function run():void
 55         {
 56             UserVO.register();
 57 
 58             _result = new Object();
 59 
 60             var gateway: String = "http://localhost:8080/mappingExample/gateway";
 61             var encoding: uint = ObjectEncoding.AMF0;
 62 
 63             var method: String = "de.websector.blog.openamf.mapping.services.UserServices.getUserByName";
 64             var responder:Responder = new Responder(onResult, onFault);
 65 
 66             super.connect(gateway, encoding);
 67 
 68             super.call(method, responder, "Luke Skywalker");
 69 
 70         }
 71 
 72         /**
 73        * Callback handler for receiving a fault
 74        * @param $result        Object
 75        *
 76        */
 77         private function onFault($result: Object):void
 78         {
 79             result.addError(this, new Error($result.toString()));
 80             // call super.run() to execute test methods
 81             super.run();
 82         }
 83 
 84         /**
 85        * Callback handler for receiving a result
 86        * @param $result        Object
 87        *
 88        */
 89         private function onResult($result: Object):void
 90         {
 91             _result = $result;
 92 
 93             super.disposeConnection();
 94             // call super.run() to execute test methods
 95             super.run();
 96         }
 97 
 98         /**
 99        * Tests the userVO
100        */
101         public function testUserVO():void
102         {
103             assertTrue("result is instance of ", _result is UserVO);
104         }
105 
106         /**
107        * Tests the userName
108        */
109         public function testUserName():void
110         {
111             assertEquals("UserName Luke Skywalker", _userVO.userName, "Luke Skywalker");
112         }
113 
114         /**
115        * Tests the registerDate
116        */
117         public function testRegisterDate():void
118         {
119             assertTrue("Register date ", _userVO.registerDate is Date);
120         }
121 //
122 //     /**
123 //      * Test that is born to lose.
124 //      */
125 //     public function testFail():void
126 //     {
127 //         assertFalse("failing test", true);
128 //     }

Download

Source including test example files: RemotingTestCaseExample.zip

Happy (asynchronous) testing! ;-)

Acknowledge

UPDATE (12/05/07)

Luke Bayes has added the RemotingTestCase to AsUnits repository. Thanks Luke!

Any feedback?

comments powered by Disqus