ホームページ >ウェブフロントエンド >jsチュートリアル >AngularJSの単体テスト:サービス、コントローラー、プロバイダー
サービスは、AngularJSアプリケーションで最も一般的なコンポーネントの1つです。それらは、中央の場所で再利用可能なロジックを定義する方法を提供し、同じロジックを何度も繰り返す必要がないようにします。サービスのシングルトンの性質により、複数のコントローラー、ディレクティブ、さらには他のサービスで同じデータを共有することができます。
サービスは、そのタスクを実行するために他のサービスのセットに依存できます。たとえば、Aという名前のサービスは、そのタスクを実行するためにサービスB、C、Dに依存します。サービスAのテスト中、依存関係B、C、およびDをモックに置き換える必要があります。$ Rootscopeや$ Parseなどの特定のユーティリティサービスを除き、通常、すべての依存関係をock笑します。 Jasmine.createspy()を使用して、テストで検査する必要がある方法(ジャスミンでは、模擬はスパイと呼ばれます)でスパイを作成します。
次のサービスを考えてみましょうこのサービスには1つの方法(showdialog)しかありません。この方法が受信する入力の値に応じて、依存関係($ windowまたはmodalsvc)として注入される2つのサービスの1つを呼び出します。
コントローラーは一般にビューに結び付けられているため、コントローラーのメソッドの動作はビューに依存します。また、ビューがコンパイルされた後、いくつかの追加のオブジェクトがスコープに追加される場合があります。これの最も一般的な例の1つは、フォームオブジェクトです。テストを予想どおりに機能させるには、これらのオブジェクトを手動で作成してコントローラーに追加する必要があります。
angular<span>.module('services', [])
</span> <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){
</span> <span>this.showDialog = function(message<span>, title</span>){
</span> <span>if(title){
</span> modalSvc<span>.showModalDialog({
</span> <span>title: title,
</span> <span>message: message
</span> <span>});
</span> <span>} else {
</span> $<span>window.alert(message);
</span> <span>}
</span> <span>};
</span> <span>}]);</span>
これで、ShowDialogメソッドの動作をテストできます。メソッドのために書くことができる2つのテストケースは次のとおりです。
<span>var mockWindow, mockModalSvc, sampleSvcObj;
</span><span>beforeEach(function(){
</span> <span>module(function($provide){
</span> $provide<span>.service('$window', function(){
</span> <span>this.alert= jasmine.createSpy('alert');
</span> <span>});
</span> $provide<span>.service('modalSvc', function(){
</span> <span>this.showModalDialog = jasmine.createSpy('showModalDialog');
</span> <span>});
</span> <span>});
</span> <span>module('services');
</span><span>});
</span>
<span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){
</span> mockWindow<span>=$window;
</span> mockModalSvc<span>=modalSvc;
</span> sampleSvcObj<span>=sampleSvc;
</span><span>}));</span>
コントローラーをテストするためのセットアッププロセスは、サービスのセットアッププロセスとはまったく異なります。これは、コントローラーが注射できないため、ルートがロードされたときに自動的にインスタンス化されるか、NGコントローラーディレクティブがコンパイルされているためです。テストでビューが読み込まれていないため、テスト中のコントローラーを手動でインスタンス化する必要があります。
$ scope
違いがわからない場合は、こちらについて詳しく読むことができます。いずれにせよ、これらの両方のケースについて説明します
次のスニペットはDataSVCサービスをock笑します:
angular<span>.module('services', []) </span> <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){ </span> <span>this.showDialog = function(message<span>, title</span>){ </span> <span>if(title){ </span> modalSvc<span>.showModalDialog({ </span> <span>title: title, </span> <span>message: message </span> <span>}); </span> <span>} else { </span> $<span>window.alert(message); </span> <span>} </span> <span>}; </span> <span>}]);</span>
$ rotscope。$ new Methodを使用して、コントローラーの新しいスコープを作成できます。コントローラーのインスタンスを作成した後、この新しい$スコープにすべてのフィールドとメソッドがあります。
コントローラーがフィールドとメソッドを$スコープに追加すると、それらが正しい値に設定されているかどうか、メソッドに正しいロジックがあるかどうかを確認できます。上記のサンプルコントローラーは、正規表現を追加して有効な番号を確認します。 正規表現の動作をテストするために仕様を追加しましょう:
<span>var mockWindow, mockModalSvc, sampleSvcObj; </span><span>beforeEach(function(){ </span> <span>module(function($provide){ </span> $provide<span>.service('$window', function(){ </span> <span>this.alert= jasmine.createSpy('alert'); </span> <span>}); </span> $provide<span>.service('modalSvc', function(){ </span> <span>this.showModalDialog = jasmine.createSpy('showModalDialog'); </span> <span>}); </span> <span>}); </span> <span>module('services'); </span><span>}); </span> <span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){ </span> mockWindow<span>=$window; </span> mockModalSvc<span>=modalSvc; </span> sampleSvcObj<span>=sampleSvc; </span><span>}));</span>
コントローラーがデフォルト値のオブジェクトを初期化した場合、仕様内の値を確認できます。
SaveDataメソッドをテストするには、BookDetailsとBookFormオブジェクトの値を設定する必要があります。これらのオブジェクトはUI要素にバインドされるため、ビューがコンパイルされた実行時に作成されます。すでに述べたように、Savedataメソッドを呼び出す前に、いくつかの値で手動でそれらを初期化する必要があります。<span>it('should show alert when title is not passed into showDialog', function(){ </span> <span>var message="Some message"; </span> sampleSvcObj<span>.showDialog(message); </span> <span>expect(mockWindow.alert).toHaveBeenCalledWith(message); </span> <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled(); </span><span>}); </span> <span>it('should show modal when title is passed into showDialog', function(){ </span> <span>var message="Some message"; </span> <span>var title="Some title"; </span> sampleSvcObj<span>.showDialog(message, title); </span> <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({ </span> <span>message: message, </span> <span>title: title </span> <span>}); </span> <span>expect(mockWindow.alert).not.toHaveBeenCalled(); </span><span>});</span>
次のスニペットはこの方法をテストします:
angular<span>.module('controllers',[]) </span> <span>.controller('FirstController', ['$scope','dataSvc', function($scope<span>, dataSvc</span>) { </span> $scope<span>.saveData = function () { </span> dataSvc<span>.save($scope.bookDetails).then(function (result) { </span> $scope<span>.bookDetails = {}; </span> $scope<span>.bookForm.$setPristine(); </span> <span>}); </span> <span>}; </span> $scope<span>.numberPattern = <span>/<span>^\d*$</span>/</span>; </span> <span>}]);</span>「コントローラーとして」のコントローラーをテストする
syntax
コントローラーを使用するコントローラーをテストするのは、$スコープを使用して構文をテストするよりも簡単です。この場合、コントローラーのインスタンスがモデルの役割を果たします。その結果、すべてのアクションとオブジェクトはこのインスタンスで利用できます。次のコントローラーを検討してください:
このコントローラーを呼び出すプロセスは、前述のプロセスに似ています。唯一の違いは、$スコープを作成する必要がないことです。
<span>module(function($provide){ </span> $provide<span>.factory('dataSvc', ['$q', function($q) </span> <span>function save(data){ </span> <span>if(passPromise){ </span> <span>return $q.when(); </span> <span>} else { </span> <span>return $q.reject(); </span> <span>} </span> <span>} </span> <span>return{ </span> <span>save: save </span> <span>}; </span> <span>}]); </span><span>});</span>このインスタンスには、コントローラー内のすべてのメンバーとメソッドが追加されるため、インスタンス参照を使用してアクセスできます。
Savedataメソッドのアサーションは同じままです。このアプローチの唯一の違いは、BookDetailsとBookformオブジェクトに値を初期化する方法です。
次のスニペットには、スペックが表示されます:
<span>beforeEach(inject(function($rootScope<span>, $controller, dataSvc</span>){ </span> scope<span>=$rootScope.$new(); </span> mockDataSvc<span>=dataSvc; </span> <span>spyOn(mockDataSvc,'save').andCallThrough(); </span> firstController <span>= $controller('FirstController', { </span> <span>$scope: scope, </span> <span>dataSvc: mockDataSvc </span> <span>}); </span><span>}));</span>
angular<span>.module('services', []) </span> <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){ </span> <span>this.showDialog = function(message<span>, title</span>){ </span> <span>if(title){ </span> modalSvc<span>.showModalDialog({ </span> <span>title: title, </span> <span>message: message </span> <span>}); </span> <span>} else { </span> $<span>window.alert(message); </span> <span>} </span> <span>}; </span> <span>}]);</span>
プロバイダーは、アプリケーションの開始前に作成する必要があるアプリケーション全体の構成のAPIを公開するために使用されます。 AngularJSアプリケーションの構成フェーズが終了すると、プロバイダーとの相互作用が許可されます。したがって、プロバイダーは、構成ブロックまたは他のプロバイダーブロックでのみアクセスできます。注入ブロックを使用してプロバイダーインスタンスを取得することはできません。むしろ、モジュールブロックにコールバックを渡す必要があります。
2番目のプロバイダー(別のプロバイダー)の定数(AppConstants)に依存する次のプロバイダーを考えてみましょう:
<span>var mockWindow, mockModalSvc, sampleSvcObj; </span><span>beforeEach(function(){ </span> <span>module(function($provide){ </span> $provide<span>.service('$window', function(){ </span> <span>this.alert= jasmine.createSpy('alert'); </span> <span>}); </span> $provide<span>.service('modalSvc', function(){ </span> <span>this.showModalDialog = jasmine.createSpy('showModalDialog'); </span> <span>}); </span> <span>}); </span> <span>module('services'); </span><span>}); </span> <span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){ </span> mockWindow<span>=$window; </span> mockModalSvc<span>=modalSvc; </span> sampleSvcObj<span>=sampleSvc; </span><span>}));</span>これをテストするには、まず依存関係をmock笑する必要があります。サンプルコードでこれを行う方法を見ることができます。
プロバイダーをテストする前に、モジュールがロードされて準備が整っていることを確認する必要があります。テストでは、注入ブロックが実行されるか、最初のテストが実行されるまでモジュールの負荷が延期されます。いくつかのプロジェクトでは、モジュールをロードするために空の最初のテストを使用するいくつかのテストを見てきました。私はこのアプローチのファンではありません。テストでは何もしておらず、テストの総数にカウントを追加します。代わりに、空の注入ブロックを使用してモジュールをロードします。
次のスニペットは参照を取得し、モジュールをロードします。参照がすべて揃ったので、プロバイダーで定義されたメソッドを呼び出してテストできます。
結論
<span>it('should show alert when title is not passed into showDialog', function(){ </span> <span>var message="Some message"; </span> sampleSvcObj<span>.showDialog(message); </span> <span>expect(mockWindow.alert).toHaveBeenCalledWith(message); </span> <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled(); </span><span>}); </span> <span>it('should show modal when title is passed into showDialog', function(){ </span> <span>var message="Some message"; </span> <span>var title="Some title"; </span> sampleSvcObj<span>.showDialog(message, title); </span> <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({ </span> <span>message: message, </span> <span>title: title </span> <span>}); </span> <span>expect(mockWindow.alert).not.toHaveBeenCalled(); </span><span>});</span>
単位テストは時々トリッキーになりますが、アプリケーションの正確性を保証するため、時間を費やす価値があります。 AngularJSを使用して、フレームワークを使用して記述されたコードを簡単にテストします。この記事が、アプリケーションのテストを拡張および強化するのに十分なアイデアを提供することを願っています。今後の記事では、コードの他の部分をテストする方法をさらに検討します。
angularjsサービス、コントローラー、およびプロバイダーに関するユニットテストに関するよくある質問(FAQ)angular<span>.module('controllers',[]) </span> <span>.controller('FirstController', ['$scope','dataSvc', function($scope<span>, dataSvc</span>) { </span> $scope<span>.saveData = function () { </span> dataSvc<span>.save($scope.bookDetails).then(function (result) { </span> $scope<span>.bookDetails = {}; </span> $scope<span>.bookForm.$setPristine(); </span> <span>}); </span> <span>}; </span> $scope<span>.numberPattern = <span>/<span>^\d*$</span>/</span>; </span> <span>}]);</span>
AngularJSテストでの非同期操作は、$ qサービスとJasmineが提供する「完了」関数を使用して処理できます。 $ Qサービスを使用すると、テストで解決または拒否できる約束を作成できます。 「完了」関数は、非同期操作が完了したことを示すために呼び出すことができます。 。 AngularJSが提供する$コンパイルサービスを使用して、ディレクティブのインスタンスを作成できます。インスタンスを作成した後、jQueryのような方法を使用して操作し、範囲への影響を確認できます。それらをock笑することで対処することができます。依存関係のモックバージョンを作成し、テスト中のコンポーネントに注入できます。これにより、依存関係の動作を制御し、テストのためにコンポーネントを分離できます。
以上がAngularJSの単体テスト:サービス、コントローラー、プロバイダーの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。