LoginSignup
3
3

More than 5 years have passed since last update.

【AngularJS × Jasmine】ディレクティブのユニットテストを書く

Posted at

scopeが分離されていない場合

例としてテストするのは、以下のティレクティブです。
ボタンをクリックすると$scope内のフラグが変化する、といったようなディレクティブです。

directive.js
app.module('myApp').directive('testDirective',() => {
    return {
      templateUrl: 'test-directive.html',
      restrict: E,
      controller: function () {
          $scope.flag = false;
          $scope.clickButton = function () {
              $scope.flag = true;
          };
      }
    };
});

以下のように書きます。

spec.js
 describe("ディレクティブのテスト", () => {

     const $scope;

     beforeEach(() => {
         inject(($rootScope, $compile, $httpBackend) => {
             // ディレクティブを生成する
             $scope = $rootScope.$new();
             const element = angular.element("<test-directive></test-directive>");
             $compile(element)($scope);

             // テンプレートを読み込むhttpリクエストに対して空のレスポンスを返す
             $httpBackend.whenGET('test-directive.html').respond();
             $httpBackend.flush();
         });
     });

    it("ボタンをクリックするとflagがtrueになる", inject(function() {
        $scope.clickButton();
        expect($scope.flag)toEqual(true);
    }));

});

主な手順としては、
1. テストするディレクティブを生成し、
2. httpリクエストが存在する場合はflush()した後にテストします。

ディレクティブの生成方法は、ご覧の通り、
1. \$scopeを新規に作り、
2. angular.element()でディレクティブを呼び出した後に、
3. \$compileで、テンプレートと\$scopeを紐付けてディレクティブを生成します。

気をつける点としては、templateUrlを使っている場合は、必ず$httpBackend.flush()をテスト前に実行する必要があります
templateUrlの呼び出しはhttpリクエストですので、これをflush()して解決しないと、先のテストができません。
もちろん、templateUrlを使わずとも、ディレクティブの呼び出し時にhttpリクエストが走る場合も同様です。

scopeが分離しているディレクティブの場合

以下のような、親と$scopeが分離しているディレクティブのケースの場合を考えます。

app.module('myApp').directive('testDirective',() => {
    return {
      scope:{
        greet: '@'
      },
      templateUrl: 'test-directive.html',
      restrict: E,
      controller: function () {
          $scope.flag = false;
          $scope.clickButton = function () {
              $scope.flag = true;
              $scope.greeting = $scope.greet;
          };
      }
    };
});
 describe("ディレクティブのテスト", () => {

     const $scope;

     beforeEach(() => {
         inject(($rootScope, $compile, $httpBackend) => {
             // ディレクティブを生成する
             $scope = $rootScope.$new();
             const element = angular.element("<test-directive greet="hello"></test-directive>");
             $compile(element)($scope);

             // テンプレートを読み込むhttpリクエストに対して空のレスポンスを返す
             $httpBackend.whenGET('test-directive.html').respond();
             $httpBackend.flush();

             // ディレクティブ内のscopeを取得する
             childScope = $scope.$scope.$$childTail;
         });
     });

    it("ボタンをクリックするとflagがtrueになる", inject(function() {
        childScope.clickButton();
        expect(childScope.flag)toEqual(true);
    }));

    it("アトリビュートで指定したgreetが適切な位置に格納される", inject(function() {
        expect(childScope.greeting)toEqual('hello');
    }));
});

気をつけるべき点としては、
1. attributeからディレクティブにデータを渡す際に、

  • @の場合はそのまま文字列で渡すこと
  • =の場合は、あらかじめ$scope内にデータを格納した後、文字列で渡すこと

例えば、上記ディレクティブのgreetが=だった場合は、

...

$scope.message = 'hello';
const element = angular.element("<test-directive greet="message"></test-directive>")

...

こうなります。

  1. また、ディレクティブ内のscopeを、$scope.$$childTailから参照しています。
    ベストなやり方では無いと思いますが、ここから参照することが可能です。
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3