1

Flex 4.5.1 で記述しているクラスの単体テストを、テストとモック フレームワークにそれぞれ FlexUnit 4 とMockolateを使用して記述しようとしています。カスタム イベントにas3-signalsを使用しています。

私が書いてテストしている機能は、 ArcGIS API for Flex内のQueryTaskクラスのラッパー クラス (QueryQueue)です。これにより、実行のために複数のクエリ タスクを簡単にキューに入れることができます。私のラッパーである QueryQueue は、すべてのクエリ応答が処理されたときにイベントを送出します。completed

インターフェイスは非常にシンプルです。

public interface IQueryQueue
{
    function get inProgress():Boolean;
    function get count():int;

    function get completed():ISignal;
    function get canceled():ISignal;

    function add(query:Query, url:String, token:Object = null):void; 
    function cancel():void;
    function execute():void;
}

使用例を次に示します。

public function exampleUsage():void
{
    var queryQueue:IQueryQueue = new QueryQueue(new QueryTaskFactory());
    queryQueue.completed.add(onCompleted);
    queryQueue.canceled.add(onCanceled);

    var query1:Query = new Query();
    var query2:Query = new Query();
    // set query parameters

    queryQueue.add(query1, url1);
    queryQueue.add(query2, url2);

    queryQueue.execute();
}

public function onCompleted(sender:Object, event:QueryQueueCompletedEventArgs)
{
    // do stuff with the the processed results
}

public function onCanceled(sender:Object, event:QueryQueueCanceledEventArgs)
{
    // handle the canceled event
}

私のテストでは、現在 QueryTaskFactory および QueryTask オブジェクトをモックしています。クエリがキューに比較的簡単に追加されることを確認するなどの簡単なテスト。

[Test(Description="Tests adding valid QueryTasks to the QueryQueue.")]
public function addsQuerys():void
{
    var queryTaskFactory:QueryTaskFactory = nice(QueryTaskFactory);
    var queryQueue:IQueryQueue = new QueryQueue(queryTaskFactory);
    assertThat(queryQueue.inProgress, isFalse());
    assertThat(queryQueue.count, equalTo(0));

    var query1:Query = new Query();
    queryQueue.add(query1, "http://gisinc.com");
    assertThat(queryQueue.inProgress, isFalse());
    assertThat(queryQueue.count, equalTo(1));

    var query2:Query = new Query();
    queryQueue.add(query2, "http://gisinc.com");
    assertThat(queryQueue.inProgress, isFalse());
    assertThat(queryQueue.count, equalTo(2));

    var query3:Query = new Query();
    queryQueue.add(query3, "http://gisinc.com");
    assertThat(queryQueue.inProgress, isFalse());
    assertThat(queryQueue.count, equalTo(3));
}

executeただし、メソッドもテストできるようにしたいと考えています。このメソッドは、キューに追加されたすべてのクエリを実行する必要があります。すべてのクエリ結果が処理されると、completedイベントが送出されます。テストでは、次のことを確認する必要があります。

  1. execute各クエリで一度だけ呼び出されます
  2. inProgress = true結果が処理されていない間
  3. inProgress = false結果が処理されたとき
  4. completed結果が処理されたときにディスパッチされます
  5. canceled呼び出されることはありません (有効なクエリの場合)
  6. キュー内で行われる処理は、クエリ結果を正しく処理してパッケージ化します

これまでのところ、主に weltraumpirat から提供された回答のおかげで、項目 1 から 5 のテストを作成できます。現在、実行テストは次のようになっています。

[Test(async, description="Tests that all queryies in the queue are executed and the completed signal is fired")]
public function executesAllQueriesInQueue():void
{
    // Setup test objects and mocks
    var query:Query = new Query();
    var mockedQueryTask:QueryTask = nice(QueryTask);
    var mockedQueryTaskFactory:QueryTaskFactory = nice(QueryTaskFactory);

    // Setup expectations
    expect(mockedQueryTaskFactory.createQueryTask("http://test.com")).returns(mockedQueryTask);
    expect(mockedQueryTask.execute(query, null)).once();

    // Setup handlers for expected and not expected signals (events)
    var queryQueue:IQueryQueue = new QueryQueue(mockedQueryTaskFactory);
    handleSignal(this, queryQueue.completed, verifyOnCompleted, 500, null);
    registerFailureSignal(this, queryQueue.canceled);

    // Do it
    queryQueue.add(query, "http://test.com");
    queryQueue.execute();

    // Test that things went according to plan
    assertThat(queryQueue.inProgress, isTrue());
    verify(mockedQueryTask);
    verify(mockedQueryTaskFactory);

    function verifyOnCompleted(event:SignalAsyncEvent, passThroughData:Object):void
    {
        assertThat(queryQueue.inProgress, isFalse());
    }
}

メソッドは次のQueryQueue.executeようになります。

public function execute():void
{
    _inProgress = true;

    for each(var queryObject:QueryObject in _queryTasks)
    {
        var queryTask:QueryTask = _queryTaskFactory.createQueryTask(queryObject.url);
        var asyncToken:AsyncToken = queryTask.execute(queryObject.query);

        var asyncResponder:AsyncResponder = new AsyncResponder(queryTaskResultHandler, queryTaskFaultHandler, queryObject.token);
        asyncToken.addResponder(asyncResponder);
    }
}

private function queryTaskResultHandler(result:Object, token:Object = null):void
{
    // For each result collect the data and stuff it into a result collection
    // to be sent via the completed signal when all querytask responses
    // have been processed.
}

private function queryTaskFaultHandler(error:FaultEvent, token:Object = null):void
{
    // For each error collect the error and stuff it into an error collection
    // to be sent via the completed signal when all querytask responses
    // have been processed.
}

queryTaskResultHandler上記のテスト 6 では、およびで返されるデータqueryTaskFaultHandlerが適切に処理されることをテストします。

つまり、completed成功と失敗の結果を含むすべてのクエリ応答が返されるまで、イベントをディスパッチしません。

このプロセスをテストするには、モックされた各クエリ タスクの結果ハンドラーとエラー ハンドラーに返されるデータをモックする必要があると思います。

AsyncResponderでは、 FlexUnit を使用して作成された結果ハンドラーに渡されたデータをモック化し、モック化するにはどうすればよいでしょうか。

4

1 に答える 1

1

mockolate を使用して、任意のオブジェクトまたはインターフェイスをモックできます。私の経験では、次のようにルールとモックを設定するのが最善です。

[Rule]
public var rule : MockolateRule = new MockolateRule();

[Mock]
public var task : QueryTask;

ルールをインスタンス化する必要がありますが、モック オブジェクトはインスタンス化しないことに注意してください。

次に、期待を指定できます。

[Test]
public function myTest () : void {
    mock( task ).method( "execute" ); // expects that the execute method be called
}

パラメータなど、さまざまなことを期待できます。

    var responder:AsyncResponder = new AsyncResponder(resultHandler, faultHandler);
    mock( task ).method( "execute" ).args( responder ); // expects a specific argument

または、オブジェクトが特定の値を返すようにします。

    mock( queue ).method( "execute" ).returns( myReturnValue ); // actually returns the value(!)

モック オブジェクトからイベントを送信するのは、それを呼び出すのと同じくらい簡単dispatchEventです。元のクラスをモックしているので、EventDispatcher.

ここで、あなたの特別なケースのために、3つの外部依存関係すべての使用QueryQueryTaskAsyncResponderをモックするのが最善だ思いますQueue.

これらのオブジェクトをキュー内で作成しているため、それらをモックするのは困難です。実際、外部依存関係がない限り、どのクラスにも直接何も作成しないでください。代わりに、作成する必要があるオブジェクトごとにファクトリ (依存性注入フレームワークを使用することをお勧めします) を渡します。その後、テスト ケースでそのファクトリをモックし、必要に応じてモック オブジェクトを返すようにできます。

public class QueryFactory {
    public function createQuery (...args:*) : Query {
       var query:Query = new Query();
       (...) // use args array to configure query
       return query;
    }
}

public class AsyncResponderFactory {
    public function createResponder( resultHandler:Function, faultHandler:Function ) : AsyncResponder {
        return new AsyncResponder(resultHandler, faultHandler);
    }
}

public class QueryTaskFactory {
    public function createTask (url:String) : QueryTask {
       return new QueryTask(url);
    }
}

... 順番待ち:

(...)
public var queryFactory:QueryFactory;
public var responderFactory : AsyncResponderFactory;
public var taskFactory:QueryTaskFactory;
(...)
var query:Query = queryFactory.createQuery ( myArgs );
var responder:AsyncResponder = responderFactory.createResponder (resultHandler, faultHandler);
var task:QueryTask = taskFactory.createTask (url);
task.execute (query, responder);
(...)

...あなたのテストで:

[Rule]
public var rule : MockolateRule = new MockolateRule();

[Mock]
public var queryFactory:QueryFactory;
public var query:Query; // no need to mock this - you are not calling any of its methods in Queue.
[Mock]
public var responderFactory:AsyncResponderFactory;
public var responder:AsyncResponder;
[Mock]
public var taskFactory:QueryTaskFactory;
[Mock]
public var task:QueryTask;

[Test]
public function myTest () : void {
    query = new Query();
    mock( queryFactory ).method( "createQuery ").args ( (...) ).returns( query ); // specify arguments instead of (...)!

    responder = new AsyncResponder ();
    mock( responderFactory ).method( "createResponder" ).args( isA(Function) , isA(Function) ).returns( responder ); // this will ensure that the handlers are really functions 
    queue.responderFactory = responderFactory;

    mock( task ).method( "execute" ).args( query, responder );
    mock( taskFactory ).method( "createTask" ).args( "http://myurl.com/" ).returns( task );
    queue.taskFactory = taskFactory; 

    queue.doStuff(); // execute whatever the queue should actually do
}

すべてのモックを として宣言する必要publicがあり、モック オブジェクトをホストに渡す前にすべての期待値を追加する必要があることに注意してください。そうしないと、mockolate はプロキシ オブジェクトを正しく構成できません。

于 2012-03-07T08:10:33.977 に答える