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

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


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

```js: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;
          };
      }
    };
});
```
以下のように書きます。

```js: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が分離しているディレクティブのケースの場合を考えます。

```js
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;
          };
      }
    };
});
```

```js
 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が`=`だった場合は、

 ```js:
...

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

...

```
こうなります。


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