>>  賞品はこちらで紹介しています 
AngularJS TIPS

AngularJS TIPS

AngularJSのディレクティブを単体テストするには?

2016年9月26日

テスティングフレームワーク「Karma+Jasmin」を使って、AngularJSの「ディレクティブ」の単体テストを記述し、それを実行する方法を解説する。

  • このエントリーをはてなブックマークに追加

 別稿1「TIPS:AngularJSアプリの単体テストを実施するには?(準備編)」+別稿2「同(実行編)」では、KarmaJasmine環境で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」に変更しているので注意してください。

JavaScript
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());
    }
  }]);
リスト1 テスト対象となるtrimフィルターのコード(/angular_tips/UnitTest/scripts/directive.js)
(2)テストスクリプトを準備する

 テストのためのコードを準備します。その基本的な方法は、別稿2で説明しています。今回はリスト2のテストコードを記述しました。

JavaScript
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);
  });
});
リスト2 ディレクティブをテストするためのコード(/angular_tips/UnitTest/spec/directive_spec.js)

 ディレクティブをテストするには、

 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コードの内容を、イベントハンドラーなどを活用して動的に切り替えるために、コントローラー付きの独自ディレクティブを作成する方法を説明する。

78. 【現在、表示中】≫ AngularJSのディレクティブを単体テストするには?

テスティングフレームワーク「Karma+Jasmin」を使って、AngularJSの「ディレクティブ」の単体テストを記述し、それを実行する方法を解説する。

GrapeCity Garage 記事内容の紹介

Azure Central の記事内容の紹介

Twitterでつぶやこう!


Build Insider賛同企業・団体

Build Insiderは、以下の企業・団体の支援を受けて活動しています(募集概要)。

ゴールドレベル

  • グレープシティ株式会社
  • 日本マイクロソフト株式会社