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>")
...
こうなります。
- また、ディレクティブ内のscopeを、
$scope.$$childTail
から参照しています。
ベストなやり方では無いと思いますが、ここから参照することが可能です。