読者です 読者をやめる 読者になる 読者になる

kkAyatakaのメモ帳。

誰かの役に立つかもしれない備忘録。

FlexUnit 4のカスタムリスナー

テスト結果を受け取るリスナーはUIListenerを初め、TextListener、TraceListenerなどが標準で用意されていますが、カスタムで作成することも可能です。Flash用に書かれていますが、Adobe AIRであればファイルを直接扱えるので、ファイル出力用のリスナーを作成するのも良いでしょう。

まあ、最終的な形はともかくとして、今回はどの程度情報が取得できるかを確認します。ファイル構成は次の通り。テストスイートの扱いを知りたいので、いくつかスイートを作って動作を見てみます。
f:id:kkAyataka:20120226222412p:plain

リスナー

TextListenerなどはRunListenerを継承しているので、RunListenerを継承したクラスを作り、メソッドをオーバーライドしながら動きを見てみます。

全メソッドをオーバライドするのであれば、本来はIRunListenerの実装クラスで良い気がします。また、testAssumptionFailureに関してはよくわかっていません。FlexUnit 4のセオリー周りと関係ありそうですけど、セオリー周りは調べてないのでなんとも。まあ、とりあえず今回は無視です。

// CustomListener.as
package {
  import org.flexunit.runner.IDescription;
  import org.flexunit.runner.notification.Failure;
  import org.flexunit.runner.notification.RunListener;
  import org.flexunit.runner.Result;

  public class CustomListener extends RunListener {
    // 全テストの前
    override public function testRunStarted(desc:IDescription):void {
      trace("runStart", desc.displayName);
    }

    // 全テストが終了
    override public function testRunFinished(result:Result):void {
      trace("runFinish");
    }

    // テストの開始
    override public function testStarted(desc:IDescription):void {
      trace("\tstart", desc.displayName);
    }

    // テストの終了
    override public function testFinished(desc:IDescription):void {
      trace("\tfinish", desc.displayName);
    }

    // テストの失敗
    override public function testFailure(fail:Failure):void {
      trace("\t\tfail");
    }

    // [Ignore]メタデータつきのテスト
    override public function testIgnored(desc:IDescription):void {
      trace("\t\tignore", desc.displayName);
    }

    // よくわからないので無視
    override public function testAssumptionFailure(fail:Failure):void {
      trace("\t\tassumption");
    }
  }
}

テストケース

適当にテストケースを準備します。成功、失敗、無視とメソッド名から影響ありそうなものを準備します。

// TestCase1.as
package suite.tests {
  import org.flexunit.Assert;

  public class TestCase1 {
    [Test(description="Test Description", issueID=12345)]
    public function testSuccess():void {
      Assert.assertEquals(Math.pow(2, 2), 4);
    }

    [Test]
    public function testFail():void {
      Assert.assertTrue(false);
    }

    [Ignore]
    [Test]
    public function ignoreTest():void {
      Assert.assertTrue(false);
    }
  }
}
// TestCase2.as
package suite.tests {
  import org.flexunit.Assert;

  public class TestCase2 {
    [Test]
    public function test2():void {
      var array:Array = new Array();
      Assert.assertEquals(array.length, 0);
    }
  }
}

テストスイート

テストスイートも適当ですが、スイートの入れ子構造の呼び出しを確認できる形にします。

// TestSuite.as
package suite {
  import suite.tests.TestCase1;

  [Suite]
  [RunWith("org.flexunit.runners.Suite")]
  public class TestSuite {
    public var t1:TestCase1;

    public var s:TestSuiteSub;
  }
}
// TestSuiteSub.as
package suite {
  import suite.tests.TestCase2;

  [Suite]
  [RunWith("org.flexunit.runners.Suite")]
  public class TestSuiteSub {
    public var t2:TestCase2;
  }
}

テストランナー

テストの実行前に、カスタムリスナーのインスタンスをaddListenerで登録します。リスナーは複数登録できるので、GUI表示しながら、traceやファイル出力が可能です。

// app.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication
  xmlns:mx="http://www.adobe.com/2006/mxml"
  xmlns:flexUnitUIRunner="http://www.adobe.com/2009/flexUnitUIRunner"
  width="1200"  height="580"
  creationComplete="creationCompleteHandler(event)">

  <mx:Script>
    <![CDATA[
      import mx.events.FlexEvent;

      import org.flexunit.listeners.UIListener;
      import org.flexunit.runner.FlexUnitCore;

      import suite.TestSuite;

      private var core:FlexUnitCore;

      private function creationCompleteHandler(event:FlexEvent):void {
        core = new FlexUnitCore;

        core.addListener(new CustomListener());
        core.addListener(new UIListener(uiListener));

        core.run(TestSuite);
      }
    ]]>
  </mx:Script>
  <flexUnitUIRunner:TestRunnerBase
    id="uiListener"
    width="100%" height="100%" />
</mx:WindowedApplication>

コンパイルと実行

コンパイルして実行すると、ターミナル上に結果が出力されます。

テストスイート自体はなんのコールバックもありません。テストスイートをベースに階層構造でテスト結果を出力したかったので少々当てが外れましたが、メソッド名がパッケージ名からのフルネームで取得できるようなので、これからパースすれば良さそうです。

$ amxmlc -library-path+=libs app.mxml 
$ adl app-desc.xml
runStart 
	start suite.tests::TestCase2.test2
	finish suite.tests::TestCase2.test2
	start suite.tests::TestCase1.testFail
		fail
	finish suite.tests::TestCase1.testFail
		ignore suite.tests::TestCase1.ignoreTest
	start suite.tests::TestCase1.testSuccess
	finish suite.tests::TestCase1.testSuccess
runFinish