FlexUnit4で非同期イベントのテスト
FlexUnit4のテストではまった事のメモ。
Async.proceedOnEventを複数使う場合は要注意。
因みに、"Asynchronous Event Received out of Order" はAsync.proceedOnEvent(が内部で呼んでいるAsync.asyncHandlerのイベントリスナ登録)の順番とイベント発生順序が違う場合に出るエラーです。
あと、UIImpersonatorはUIのにせもの。擬似的にmxmlコンポーネントを生成するために、テスト対象のCanvas(UIComponent)をUIImpersonatorの子に追加しています。
HogeComponent.mxml (テスト対象クラス)
<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="200" height="70"> <mx:TextInput id="t1" maxChars="10" /> <mx:TextInput id="t2" maxChars="10" /> </mx:Canvas>
HogeComponentTest.as
package tmftake { import mx.events.FlexEvent; import org.flexunit.async.Async; import org.fluint.uiImpersonation.UIImpersonator; public class HogeComponentTest { private const LONG_TIME:int = 5000; private var hoge:HogeComponent; [Before(async, ui)] public function setUp():void { //カスタムコンポーネント生成 hoge = new HogeComponent(); //イベント発生待機 Async.proceedOnEvent(this, hoge, FlexEvent.CREATION_COMPLETE, LONG_TIME); //UIImporsonatorに追加 UIImpersonator.addChild(hoge); } [After(async, ui)] public function tearDown():void { //UIImporsonatorから削除 UIImpersonator.removeChild(hoge); hoge = null; } [Test(async, ui)] public function testUIAsync1():void { //イベント発生待機 Async.proceedOnEvent(this, hoge.t1, FlexEvent.VALUE_COMMIT, LONG_TIME); hoge.t1.text = "test"; } [Test(async, ui)] public function testUIAsync2():void { //イベント発生待機 Async.proceedOnEvent(this, hoge.t1, FlexEvent.VALUE_COMMIT); hoge.t1.text = "test"; //イベント発生待機 Async.proceedOnEvent(this, hoge.t2, FlexEvent.VALUE_COMMIT, LONG_TIME); hoge.t2.text = "test"; } [Test(async, ui)] public function testUIAsync3():void { //カスタムコンポーネント生成 var hogeLocal:HogeComponent = new HogeComponent(); //イベント発生待機 Async.proceedOnEvent(this, hogeLocal, FlexEvent.CREATION_COMPLETE, LONG_TIME); //UIImporsonatorに追加 UIImpersonator.addChild(hogeLocal); //イベント発生待機 Async.proceedOnEvent(this, hogeLocal.t1, FlexEvent.VALUE_COMMIT); hogeLocal.t1.text = "test"; } [Test(async, ui)] public function testUIAsync4():void { //カスタムコンポーネント生成 var hogeLocal:HogeComponent = new HogeComponent(); //UIImporsonatorに追加 UIImpersonator.addChild(hogeLocal); //イベント発生待機 Async.proceedOnEvent(this, hogeLocal.t1, FlexEvent.VALUE_COMMIT); hogeLocal.t1.text = "test"; } } }
結果
tmftake::HogeComponentTest.testUIAsync1 . tmftake::HogeComponentTest.testUIAsync2 . tmftake::HogeComponentTest.testUIAsync3 . tmftake::HogeComponentTest.testUIAsync3 F tmftake::HogeComponentTest.testUIAsync4 . Time: 7.064 There was 1 failure: 1 tmftake::HogeComponentTest.testUIAsync3 Error: Asynchronous Event Received out of Order at org.flexunit.internals.runners.statements::ExpectAsync/handleAsyncEventFired()[C:\Users\dmoore\Documents\_Production\Flex Unit 4\GIT\FlexUnit4\src\org\flexunit\internals\runners\statements\ExpectAsync.as:399] at flash.events::EventDispatcher/dispatchEventFunction() at flash.events::EventDispatcher/dispatchEvent() at org.flexunit.async::AsyncHandler/handleEvent()[C:\Users\dmoore\Documents\_Production\Flex Unit 4\GIT\FlexUnit4\src\org\flexunit\async\AsyncHandler.as:162] at flash.events::EventDispatcher/dispatchEventFunction() at flash.events::EventDispatcher/dispatchEvent() at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298] at mx.controls::TextInput/set text()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\TextInput.as:1517] at tmftake::HogeComponentTest/testUIAsync3()[D:\workspace\FlexUnit4Sample\src\tmftake\HogeComponentTest.as:59] at Function/http://adobe.com/AS3/2006/builtin::apply() at flex.lang.reflect::Method/apply()[C:\Users\dmoore\Documents\_Production\Flex Unit 4\GIT\FlexUnit4\src\flex\lang\reflect\Method.as:208] at ReflectiveCallable/run()[C:\Users\dmoore\Documents\_Production\Flex Unit 4\GIT\FlexUnit4\src\org\flexunit\runners\model\FrameworkMethod.as:297] at org.flexunit.runners.model::FrameworkMethod/applyExplosivelyAsync()[C:\Users\dmoore\Documents\_Production\Flex Unit 4\GIT\FlexUnit4\src\org\flexunit\runners\model\FrameworkMethod.as:171] at org.flexunit.runners.model::FrameworkMethod/invokeExplosivelyAsync()[C:\Users\dmoore\Documents\_Production\Flex Unit 4\GIT\FlexUnit4\src\org\flexunit\runners\model\FrameworkMethod.as:186] at org.flexunit.internals.runners.statements::InvokeMethod/evaluate()[C:\Users\dmoore\Documents\_Production\Flex Unit 4\GIT\FlexUnit4\src\org\flexunit\internals\runners\statements\InvokeMethod.as:77] at org.flexunit.internals.runners.statements::ExpectAsync/evaluate()[C:\Users\dmoore\Documents\_Production\Flex Unit 4\GIT\FlexUnit4\src\org\flexunit\internals\runners\statements\ExpectAsync.as:537] at org.flexunit.internals.runners.statements::StackAndFrameManagement/handleTimerComplete()[C:\Users\dmoore\Documents\_Production\Flex Unit 4\GIT\FlexUnit4\src\org\flexunit\internals\runners\statements\StackAndFrameManagement.as:141] at flash.events::EventDispatcher/dispatchEventFunction() at flash.events::EventDispatcher/dispatchEvent() at flash.utils::Timer/tick() FAILURES!!! Tests run: 4, 1 Failures:
testUIAsync2では成功なのに、testUIAsync3では失敗なのが納得行かなかった。
Async.proceedOnEvent関数のソースを覗くと、pendUntilCompleteという関数でイベントリスナ登録をしている。メソッド名を見る限りは、そのイベントが発生するまで待機するような動作を想定していたんだけど…。
よくよく考えたら、非同期処理が完了するまでテストメソッドが勝手に待機できるわけないですね。根本的に考え方が間違っていた様です。
順序が保証されない複数イベントが発生しうるメソッドのテストの際にはAsync.proceedOnEventを複数書かないほうが良いようです。