AngularJS TIPS
AngularJSのディレクティブを単体テストするには?
テスティングフレームワーク「Karma+Jasmin」を使って、AngularJSの「ディレクティブ」の単体テストを記述し、それを実行する方法を解説する。
別稿1「TIPS:AngularJSアプリの単体テストを実施するには?(準備編)」+別稿2「同(実行編)」では、Karma+Jasmine環境でAngularJSアプリをテストする基本的な手順について解説しました。本稿では、引き続いてKarma+JasmineでAngularJSのディレクティブをテストする方法について解説します。
(1)テスト対象のコード
テスト対象のコードには、別稿3「TIPS:自作ディレクティブの属性にAngular式や関数を設定するには?」で紹介したwgHelloScope
ディレクティブを採用します。別稿3ではdirective.html
ファイルを作成していますが、本稿で説明するテスト用に、/angular_tips/UnitTest/scripts
フォルダー内にdirective.js
ファイルを新規作成し、別稿3のdirective.html
ファイルで示しているJavaScriptコード部分のみをdirective.js
ファイルにコピーしてください。具体的にはリスト1のようになります(コードの詳細は別稿3を参照してください)。なお、今回のサンプルコードではモジュール名を「myApp」から「myApp.directive」に変更しているので注意してください。
angular.module('myApp.directive', [])
.directive('wgHelloScope', function() {
return {
restrict: 'E',
scope: {
// 1Angular式や関数を指定
type: '=wgType',
name: '@wgName',
mouseover: '&wgMouseover'
},
template: '<p style="background-color:Yellow" ng-mouseover="mouseover()">'
+ '{{type}}、{{name}}さん!</p>'
}
})
.controller('MyController', ['$scope', function($scope) {
$scope.greeting = 'おはようございます',
$scope.onmouseover = function() {
console.log(new Date());
}
}]);
|
(2)テストスクリプトを準備する
テストのためのコードを準備します。その基本的な方法は、別稿2で説明しています。今回はリスト2のテストコードを記述しました。
describe('wgHelloScopeディレクティブのテスト', function() {
var $scope, element, link;
beforeEach(module('myApp.directive'));
// 1$compileサービスでコンパイル
beforeEach(inject(function(_$compile_, _$rootScope_) {
var $compile = _$compile_;
var $rootScope = _$rootScope_;
element = angular.element('<wg-hello-scope wg-type="data" wg-name="山田太郎" wg-mouseover="onmouseover()"></wg-hello-scope>');
link = $compile(element);
// 2対象の要素とスコープとをひも付け
$scope = $rootScope.$new(true);
link($scope);
}));
it('ディレクティブの結果を確認する', function() {
var result = 'おはようございます、山田太郎さん!';
var result2 = 'こんにちは、山田太郎さん!';
$scope.data = 'おはようございます';
// 3スコープの内容をディレクティブに反映
$scope.$digest();
expect(element.text()).toEqual(result);
$scope.onmouseover = function() {
$scope.data = 'こんにちは';
}
$scope.onmouseover();
$scope.$digest();
expect(element.text()).toEqual(result2);
});
});
|
ディレクティブをテストするには、
1ディレクティブのコンパイル
↓
2スコープへのリンク
↓
3$digestループ*1
という内部的な処理をコードから明示的に指示しなければなりません。
- *1 $digestループとは、モデルとビュー(テンプレート)とを同期させるための仕組みです。詳細は後日別稿にて解説の予定です。
まず1の$compile
サービスで行っているのが、「ディレクティブのコンパイル」の部分です。
[構文]$compileサービス
$compile(element [, transclude [,priority]])
- element: コンパイルする要素(文字列、jqLiteオブジェクト)
- transclude: ディレクティブで利用できる関数(非推奨)
- priority: 優先順位
$compile
サービスの戻り値は、link
関数です。link
関数は、$scope
と要素とをリンクするためのものです。これに$scope
オブジェクトを渡すことで、対象の要素とスコープとをひも付けることができます(2スコープへのリンク)。
ここまではディレクティブを動作させるための準備なので、beforeEach
メソッドで初期化処理として定義しておきます。
以降は、個々のテストケース(it
メソッド)の役割です。テストケースでは、$scope
オブジェクトに必要なデータをセットした上で、$digest
メソッドでスコープの内容をディレクティブに反映させます(3digestループ)。これで、ディレクティブはスコープに応じた出力を生成するはずなので、toEqual
メソッドで要素配下のテキストが期待したものであるかを判定します。要素配下のテキストは、jqLiteオブジェクトのtext
メソッドで取得できます。
ディレクティブの「コンパイル」→「スコープへのリンク」→「digestループ」という流れは、原始的でAngularJSの内部的な挙動を意識しなければならないので、やや難しく感じるかもしれません。しかし、覚えてしまえば定型的なコードにすぎませんので、イディオムとして身に付けてしまいましょう。
(3)テストスイートを実行する
テスト実行の手順は、別稿2を参照してください。
処理対象:Karma+Jasmine カテゴリ:基本
処理対象:ディレクティブ カテゴリ:基本
API:angular.module カテゴリ:ng(コアモジュール) > function(関数)
API:$injector カテゴリ:auto > service(サービス)
API:$compile|$rootScope カテゴリ:ng > service(サービス)
API:$rootScope.Scope カテゴリ:ng > type(型)
※以下では、本稿の前後を合わせて5回分(第74回~第78回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
74. ディレクティブ配下のコンテンツをテンプレートに反映させるには?(transcludeプロパティ)
自作ディレクティブ呼び出し側で指定した「配下のコンテンツ」をテンプレートに反映させることで、そのディレクティブ要素の下に埋め込まれるHTMLコードを動的に切り替える方法を説明する。
75. ディレクティブで属性を設定するには?(scopeプロパティ)
自作ディレクティブ呼び出し側で指定した「属性の値(文字列)」をテンプレートに反映させることで、そのディレクティブ要素の下に埋め込まれるHTMLコードを動的に切り替える方法を説明する。
76. 自作ディレクティブの属性にAngular式や関数を設定するには?(scopeプロパティ)
自作ディレクティブ呼び出し側で指定した「属性の値(Angular式や関数)」をテンプレートに反映させることで、そのディレクティブ要素の下に埋め込まれるHTMLコードを動的に切り替える方法を説明する。
77. 自作ディレクティブの挙動を定義するには?(controller/controllerAs/bindToControllerプロパティ)
テンプレートに基づき出力されるHTMLコードの内容を、イベントハンドラーなどを活用して動的に切り替えるために、コントローラー付きの独自ディレクティブを作成する方法を説明する。