2015-10-07 3 views
22

あなたが書いた単体テストの大量を持っている私たちの多くが、これが解決できないほど問題を抱えていないことをすでに知っているかもしれません。私はAngularJs unit testingガイドの後にJasmine構文で書かれた約3500以上のユニットテストを持っています。テストはKarmaランナーで実行されます。AngularJsユニットのテストメモリリーク

問題は、メモリリークのために一度に実行できないという問題です。それらを実行している間、メモリはどんなブラウザで実行されていても、ある時点でブラウザがクラッシュしたり切断されたりしても構築されます。この問題を抱えているコミュニティで使用されている、私が今認識している最良の回避策は、テストを複数回実行し、最後に1回の実行結果をマージして正しいカバレッジを得ることです。

私がこの問題で初めて会ったとき、私は約1000回のテストを受けました。使用可能なすべてのブラウザを試してみた後、私は複数のテストを分割しましたが、これは長い間、良い回避策ではないことが判明しました。テストは完了までの時間を短縮するために並行して実行される14回以上のシングルランで実行されますが、IMOではこれを永久に解決することはできませんが、リソース制限(RAM、CPU)

ブラウザでアプリケーションを実行しているときに問題が発生していないにもかかわらず、私のコードにメモリリークがあると主張する人がいます。それで、私はこの問題を浮き彫りにするプロジェクトの例を作りました。私は多くのDOM要素を初期化するために、このサービスを使用する簡単なdirectiveを持っている。その後

app.factory('heavyLoad', function() { 
    // init 
    var heavyList = []; 
    var heavyObject = {}; 
    var heavyString = ''; 

    // populate.. 

    return { 
    getHeavyList: function() { return heavyList; }, 
    getHeavyObject: function() { return heavyObject; }, 
    getHeavyString: function() { return heavyString; } 
    }; 
}); 

:この問題を再現するために、私は、このようなメモリ消費量で重くなっ角度serviceを作成していますがあり

app.directive('heavyLoad', function (heavyLoad) { 
    return { 
    scope: {}, 
    template: '' + 
    '<div>' + 
    ' <h1>{{title}}</h1>' + 
    ' <div ng-repeat="item in items">' + 
    ' <div ng-repeat="propData in item">' + 
    '  <p>{{propData}}</p>' + 
    ' </div>' + 
    ' </div>' + 
    '</div>', 
    link: function (scope, element) { 
     scope.items = heavyLoad.getHeavyList(); 
     scope.title = heavyLoad.getHeavyString(); 

     // add data to the element 
     element.data(heavyLoad.getHeavyList()); 
    } 
    }; 
}); 

最後に、unit testingガイドで提案されているようにbtwが記述されている指示文に対して、test definitionという1000個のテストスイートを動的に登録しています。

// define multiple suits with the same definition just for showcase 
for (var i = 0; i < 1000; i += 1) { 
    describe('heavyLoad directive #' + i, testDefinition); 
} 

だけGitHubからプロジェクトをチェックアウトし、カルマを実行する前に実行開始の例を試してみる:

$ npm install 
$ bower install 

私は、問題がどこにあるか見つけ、最終的にそれを解決するのを楽しみにしていますが。

乾杯

答えて

19

問題は、各テストの後に行われる必要が忘れクリーンアップしていました。 メモリ消費量が安定しており、テストをどのブラウザーでも実行できるため、テストの数を追加しても問題はありません。

以前のテスト定義hereに、3000個のdinamically registeredテストを成功裏に実行したソリューションの変更を追加しました。

describe('testSuite', function() { 
    var suite = {}; 

    beforeEach(module('app')); 

    beforeEach(inject(function ($rootScope, $compile, heavyLoad) { 
     suite.$rootScope = $rootScope; 
     suite.$compile = $compile; 
     suite.heavyLoad = heavyLoad; 
     suite.$scope = $rootScope.$new(); 

     spyOn(suite.heavyLoad, 'getHeavyString').and.callThrough(); 
     spyOn(suite.heavyLoad, 'getHeavyObject').and.callThrough(); 
     spyOn(suite.heavyLoad, 'getHeavyList').and.callThrough(); 
    })); 

    // NOTE: cleanup 
    afterEach(function() { 
     // NOTE: prevents DOM elements leak 
     suite.element.remove(); 
    }); 
    afterAll(function() { 
     // NOTE: prevents memory leaks because of JavaScript closures created for 
     // jasmine syntax (beforeEach, afterEach, beforeAll, afterAll, it..). 
     suite = null; 
    }); 

    suite.compileDirective = function (template) { 
     suite.element = suite.$compile(template)(suite.$scope); 
     suite.directiveScope = suite.element.isolateScope(); 
     suite.directiveController = suite.element.controller('heavyLoad'); 
    }; 

    it('should compile correctly', function() { 
     // given 
     var givenTemplate = '<div heavy-load></div>'; 

     // when 
     suite.compileDirective(givenTemplate); 

     // then 
     expect(suite.directiveScope.title).toBeDefined(); 
     expect(suite.directiveScope.items).toBeDefined(); 
     expect(suite.heavyLoad.getHeavyString).toHaveBeenCalled(); 
     expect(suite.heavyLoad.getHeavyList).toHaveBeenCalled(); 
    }); 

}); 

をクリーンアップする必要が二つあります:ここで

は、テストが今どのように見えるかですテストディレクティブ用にコンパイル$を使用して

  • コンパイル要素が
  • 記述関数スコープ内のすべての変数

これらの2つはtricあなたのことを知りたいと思っています。 最初のものはすでに知っていましたが、Jasmineがどのように内部で働くかに関連する2番目のものが見つかるまで、それはあまり助けになりませんでした。 私は自分のGitHubリポジトリにissueを作成しました。これは、より良い解決策を見つけるのに役立ち、少なくともこの情報を開発者間でより速く広めます。

この回答がこの問題を抱えている多くの人々に役立つことを願っています。他のすべてのテストをリファクタリングした後も、私はいくつかの情報を書きます。

乾杯!

+1

成功したリファクタリングの結果は〜2分で〜4000テストが実行されます。 –

+0

どうしたことができましたか?私はジャスミンでクリーンアップに 'this'を使って実行する前のテストを設定しましたが、600テストを実行した後、ランナーはPhantomJSで失敗します。同じことが起こりますが、Chromeではテストが少なくて済みました。 –

+2

それに加えて、1.5.1では 'beforeEach'の代わりに' beforeAll'を使用して記述ごとにモジュールをモックすることができ、パフォーマンスをさらに向上させることができます。 https://docs.angularjs.org/guide/unit-testing#using-beforeall- – vivascau