2

ライブラリフォーム jQuery を Q.js に移植し、同時にユニットテストを QUnit から Jasmine.js に書き直しましたが、現在いくつかの問題に直面しています:

  • すべての promise オブジェクトが async (setTimeout( func, 0 )) で解決されるという事実のため、「runs」と「waitsFor」を使用して、あまり良くない単体テストを作成する必要があります。
  • 「jasmine.Clock.useMock」アプローチが機能していません(2週間前にリースされました)

私の質問は、Jasmine.js で Q.js を使用するライブラリをテストする方法です。

更新 1:ここで 私の単体テストを見つけることができます。テストを参照してください「キットの "取得" メソッドにより、単一レコードの読み込みが可能です」 ライブラリは、Dynamics CRM 2011 のコンテキストでの CRUD 操作に使用されます。

更新 2: setTimeout が私の唯一の「問題」ではないようです。Q.js は、非同期操作を解決するために「setImmediate」または「MessageChannel」または「setTimeout」を使用します。

4

1 に答える 1

2

単体テストを行う場合は、クロックを気にする必要はありません。

次のようなものがあるとします。

var obj = {
  functionToTest: function () {
    callServer().then(function () {
      //success
    }, function () {
      //error
    })
  }
}

テストするには、クロックを変更する必要があります。これを回避する 1 つのオプションは、timeout を変更して関数を同期的に解決することです。

var aux = window.setTimeout; //save to restore later
window.setTimeout = function(func){
  func();
};

ほとんどの場合、偽のタイムアウトを使用しても同じ結果が得られますが、失敗する場合もあります。

var obj = {
  functionToTest: function () {
    var foo = 'bar';
    callServer().then(function () {
      alert(foo);
    }, function () {
      //error
    })
    foo = 'buz';
  }
}

通常の使用では警告buzが表示されますが、偽のタイムアウトでは警告が表示されbarます。このオプションは注意して使用してください。

Q プロミスを使用する場合、成功のコールバックとエラーのコールバックを指定しているため、テスト対象の関数に含める必要はありません。

var obj = {
  functionToTest: function () {
    callServer().then(this.success, this.error)
  },

  success: function () {
    //success
  }
  error: function () {
    //error
  }
}

ここで、偽のタイムアウトを使用して、成功関数とエラー関数が呼び出されることをテストし、成功関数とエラー関数をテストできます。

コールバックでテストされた関数からのデータが必要な場合は、クロージャーを使用できます。

var obj = {
  functionToTest: function () {
    var data = 'foo';
    callServer().then(this.success(data), this.error)
  },

  success: function (data) {
    var that = this;
    return function () {
      //we have access to data and the object
    }
  }
  error: function () {
    //error
  }
}

単体テストでは、指定された入力に対する関数の出力を確認します。テストされた関数がプロミスを返す関数を呼び出している場合、テストされた関数の出力がその関数への呼び出しであることを意味するため、テストする必要があります。後でコールバックをテストする必要があります。

この例では、予想されるデータで callServer を呼び出していることをテストしてから、コールバックをテストします。

"runs" と "waitsFor" (または他の同様のオプション) を使用する必要がある場合は、単体テストを行っているのではなく、機能テストまたは統合テストを行っていることを意味します。

単体テストでは、プロジェクトの各部分が正しく機能していることを確認し、統合テストでは、すべての部分が連携して正常に機能することを確認します。

単体テストが複雑すぎる場合は、プログラム内容を変更してテスト可能にする必要があることを意味します。

編集:

あなたがコメントしたテストについて、私は次のようにします:

var aux;
beforeEach(function () {
    aux = window.setTimeout;

    window.setTimeout = function(func){
      func();
    };
});

afterEach(function () {
    window.setTimeout = aux;
});


it('should return a single object', function () {
    CrmRestKit.Retrieve(entitySchemaName, fakeid, columns).then(function (data) {
        expect(data.d).not.toBeArray();
    });
});

it('will return the fake-account', function () {
    CrmRestKit.Retrieve(entitySchemaName, fakeid, columns).then(function (data) {
        expect(data.d).toBe(fakeAccount);
    });
});

編集2:

私が setTimeout ソリューションで提供していたのは、迅速な回避策でしたが、最も純粋な「単体テスト」ソリューションではありませんでした。多くの場合、それは機能し、テストをシンプルに保ちますが、今回はそうではありません。

テストでは、コードに加えて Q と ajax の実装をテストしているため、テストが複雑になっています。

高速ソリューションが改善されました。

式から promise の実装を削除し、それが機能すると考えてみましょう。

あなたは現在、約束から、ajax呼び出しから受け取るデータを取得することを期待しています。promise の "resolve" 関数が期待されるデータで呼び出されることをテストしてみませんか?

単体テスト ソリューション:

サード パーティの実装をテストしないでください。

3 つのテストを行います。1 つは Retrieve が promise を返すことを確認するため、2 つ目はサーバーが必要とするデータを使用して ajax を呼び出していることを確認するため、最後にコールバック関数で解決を呼び出すことを確認するためです。期待されるデータを使用します(コールバック テストに対する私の最初の回答を参照してください)。

于 2013-07-24T10:32:25.773 に答える