よく忘れるので整理。
以下のようなFooComponentをテストする場合。
/** * 初期化されたらステータスをactiveにし、タイトルを描画する * タイトル下部に外部から渡されたテキストを描画する */ class FooController { private active: boolean = false; /*@ngInject*/ constructor(private $element) { } $onInit() { this.active = true; this.render(); } render() { const elm = this.$element.find('h1'); elm.text('foo'); } doSomething() { return 'Do something'; } } const FooComponent: ng.IComponentOptions = { template: '<h1></h1><div>{{ $ctrl.bar }}</div>', controller: FooController, bindings: { bar: '<' } }; export default FooComponent;
$componentControllerを利用
describe('FooComponent', () => { let controller; beforeEach(angular.mock.module('app')); beforeEach(inject(($componentController, $rootScope) => { const locals = { $scope: $rootScope.$new(), /** * Componentが$elementを利用している場合、localsに追加する必要がある * * https://docs.angularjs.org/api/ngMock/service/$componentController * > If you are using $element or $attrs in the controller, * > make sure to provide them as locals. */ $element: angular.element('<div></div>') }; const bindings = { bar: 'bar' }; controller = $componentController('foo', locals, bindings); })); it('should be active', () => { expect(controller.active).toBeFalsy(); // $onInitは直接実行 controller.$onInit(); expect(controller.active).toBeTruthy(); }); it('doSomethingを実行', () => { expect(controller.doSomething).toBeDefined(); expect(controller.doSomething()).toBe('Do something'); }); });
コントローラーのテストのみ可能。
https://docs.angularjs.org/guide/component
$compileを利用
describe('FooComponent', () => { let element; let parentScope; beforeEach(angular.mock.module('app')); beforeEach(inject(($compile, $rootScope) => { parentScope = $rootScope.$new(); // element = angular.element('<foo bar="bar"></foo>'); // element = $compile(element)(parentScope); // こっちでも問題ない? element = $compile('<foo bar="bar"></foo>')(parentScope); parentScope.bar = 'bar'; parentScope.$digest(); })); it('bindingsされたbarがhtmlに反映される', () => { expect(element.find('div').text()).toBe('bar'); }); it('$onInit -> render -> タイトルが変更される', () => { // 実際のComponent lifecycleと同じで、$onInitは自動実行される expect(element.find('h1').text()).toBe('foo'); }); it('doSomethingを実行', () => { // controllerを取得 const controller = element.controller('foo'); expect(controller.doSomething).toBeDefined(); expect(controller.doSomething()).toBe('Do something'); }); });
実際の挙動に近い形でコンポーネントのテストが可能。
https://docs.angularjs.org/guide/unit-testing
参考
- https://velesin.io/2016/08/23/unit-testing-angular-1-5-components/
- $componentController, $compileどちらを使うべきか、の話
- $compileを利用したコンポーネントテストのサンプルがまとまっている
- https://github.com/Puigcerber/angular-unit-testing
- Directives以外も含め、ユニットテストのコードがまとまっている