Angular4(Angular2~)のユニットテスト【Angularのユニットテストの基本とComponentの簡単なテスト編】
Angularのユニットテストを勉強中なので、何回かに分けて記事にする。
今回の記事は、以下の公式ドキュメントの4つのセクションをかなり参考にさせていただいている。
Introduction to Angular testing
The first karma test
Test a component
Test a component with an external template
今回は、Angularのテストの大まかな流れと、簡単なComponentのテストを記事の対象とする。
使うテストツール
Angularのテストでは、JavaScript界のいくつかのテストツールを使用する。それらのツールを理解していないと挫折するので、まずは、Angular自体のテストに先立って、それらのツールを学ぶ必要がある。以下を参考にするとわかりやすい。
jasmine
JavaScript 向けテスティングフレームワーク
jasmine公式
https://jasmine.github.io/2.0/introduction.html
jasmineのわかりやすい記事
Jasmine使い方メモ - Qiita
Jasmine spec覚え書き - Qiita
karma
ブラウザでユニットテストを実行するためのテストランナー
karmaのわかりやすい記事
step by stepで始めるKarma - Qiita
karma公式
Karma - Spectacular Test Runner for Javascript
被テストComponent
import {Component} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'テストだよ';
/**
* h1の文章を変更する
*/
changeH1Element() {
this.title = 'クリックされたぜ!';
}
}
<h1>
{{title}}
</h1>
<p class="button" (click)="changeH1Element()">ここをクリックして</p>
タイトルがあって、「ここをクリックして」というところを押すと、タイトルが変更される。
なお、今回の目的はAngularのユニットテストの学習のため、CSSなどによる装飾等は一切行わない。
テストクラス
import {ComponentFixture, TestBed, async, ComponentFixtureAutoDetect,} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {DebugElement} from '@angular/core';
import {AppComponent} from './app.component';
// describeでテストSuiteを作成
describe('AppComponentのテスト', () => {
// テストの中のAppComponent
let comp: AppComponent;
// ComponentFixtureは、 componentのインスタンスそのものとcomponentのDOM elementのハンドルであるDebugElementへのアクセスを提供する。
let fixture: ComponentFixture<AppComponent>;
// ComponentのDOM elementのhandle
let de: DebugElement;
let el: HTMLElement;
// 各Spec(個々のテスト)が開始される前に行う処理を設定する。
// 非同期処理
// Componentのインスタンスを生成する前に、Angular template compilerが外部ファイルを読み込む
beforeEach(async(() => {
// テストしたいクラスのためのモジュール環境をconfigureTestingModuleメソッドで設定する。
// メタデータの登録
TestBed.configureTestingModule({
// テストされるComponentを登録
declarations: [
AppComponent
],
providers: [
{provide: ComponentFixtureAutoDetect, useValue: true}
]
}).compileComponents(); // 外部ファイルをコンパイル
}));
// 同期処理
// Componentのインスタンスを生成
beforeEach(() => {
// ComponentFixtureは、 componentのインスタンスそのものとcomponentのDOM elementのハンドルであるDebugElementへのアクセスを提供する。
fixture = TestBed.createComponent(AppComponent);
// テストされるComponentのインスタンス
comp = fixture.componentInstance;
// queryは、fixtureのDOM全体の中から、引数で与えたelementを満たす最初のDom elementとマッチしたDebugElementを返す
// Byでは、cssのselectorを生成している。
de = fixture.debugElement.query(By.css('h1'));
el = de.nativeElement;
});
// itでSpecを作成
it('AppComponentのインスタンスが生成できているかどうか', async(() => {
// trueかどうかs
expect(comp).toBeTruthy();
}));
it('何もしない場合のタイトルがAppComponentのtitleと同じかどうか', async(() => {
expect(el.textContent).toContain(comp.title);
}));
it('何もしない場合のタイトルがAppComponentのtitleと同じかどうか(上のテストと同じことをしている)', async(() => {
expect(el.textContent).toContain('テストだよ');
}));
it('detectChangesが1回起きた時、h1の値が変更されるかどうか', async(() => {
comp.title = 'クリックされたぜ!';
fixture.detectChanges();
expect(el.textContent).toContain(comp.title);
}));
it('changeH1Elementが呼び出されたら、titleが変更されるかどうか', async(()=> {
comp.changeH1Element();
expect(comp.title).toBe('クリックされたぜ!');
}));
});
各々の処理で行っていることは、コード中にコメントとして記述してある。
Angularのテストの大まかな構成
基本的には、jasmineの構成に則っているようである。
- beforeEachで、各テストSpecの処理の前に行う処理を記述する。(場合によってはafterEachで、各テストSpecの処理の後に行う処理を記述する。)
- descibeでSuite(テストの塊のようなもの)を作成し、その中でitでSpec(個々のテストメソッド)を複数記述する。このitの中で、様々なテストを行う。
Angularのテストで使用するクラスとメソッド
TestBed
公式APIリファレンス
https://angular.io/docs/ts/latest/api/core/testing/index/TestBed-class.html
Configures and initializes environment for unit testing and provides methods for creating components and services in unit tests.
引用元:https://angular.io/docs/ts/latest/api/core/testing/index/TestBed-class.html
(意訳)
ユニットテスト用の設定と環境の初期化を行う。
また、ユニットテスト内のcomponentとserviceを生成するためのメソッドを作成する。
It creates an Angular testing module—an @NgModule class—that you configure with the configureTestingModule method to produce the module environment for the class you want to test.
引用元: https://angular.io/docs/ts/latest/guide/testing.html#!%23q-spec-file-location
(意訳)
TestBedは、Angularのテスティングモジュール(@NgModuleクラス)を生成する。
@NgModuleクラスはテストしたいクラスのためのモジュール環境をconfigureTestingModuleメソッドで設定する。
=> テストの環境初期化と設定を行う。
TestBed.configureTestingModule
テストしたいクラスのためのモジュール環境を設定する。
メタデータの登録を行う。@NgModuleのテスト版のようなもの
beforeEach (jasmineのメソッド)
各Spec(個々のテスト)が開始される前に行う処理を設定する。
TestBed.createComponentとComponentFixture
TestBed.createComponent 公式APIリファレンス
https://angular.io/docs/js/latest/api/core/testing/index/TestBed-class.html
ComponentFixture 公式APIリファレンス
https://angular.io/docs/ts/latest/api/core/testing/index/ComponentFixture-class.html
The createComponent method returns a ComponentFixture, a handle on the test environment surrounding the created component. The fixture provides access to the component instance itself and to the DebugElement, which is a handle on the component's DOM element.
引用元: https://angular.io/docs/ts/latest/guide/testing.html#!%23component-fixture
(意訳)
createComponent(TestBed.createComponent)は、生成されたcomponentのテスト環境のハンドルであるComponentFixtureクラスを返す。ComponentFixtureは、 componentのインスタンスそのものとcomponentのDOM elementのハンドルであるDebugElementへのアクセスを提供する。
⇒ ComponentFixture = テストしたいComponentのインスタンスとそのComponentのDebugElementへのアクセスを提供するもの。
しつこいようだが、これによってテスト対象のComponentのインスタンスとDOM elementを操作できるようになる。
TestBed.createComponentは、TestBedインスタンスの設定を閉じるので、createComponentを使用した後にTestBedの設定系のメソッドを使用してはいけないようである。
DebugElement.query
公式APIリファレンス
https://angular.io/docs/ts/latest/api/core/index/DebugElement-class.html
queryは、fixtureのDOM全体の中から、引数で与えたelementを満たす最初のDom elementとマッチしたDebugElementを返す。fixture.debugElement.queryのDebugElementと戻り値のDebugElementは異なる。
queryAllの場合は、引数で与えたelementを満たす全てのDom elementの配列を返す。
参考
https://angular.io/docs/ts/latest/guide/testing.html#!%23component-fixture
https://angular.io/docs/js/latest/api/core/index/DebugElement-class.html#!%23query-anchor
By
AngularのUtilityで、 predicates(述語と単純に訳していいのか不明)を生成する。
公式APIリファレンス
https://angular.io/docs/ts/latest/api/platform-browser/index/By-class.html
detectChanges
componentのchange detection cycleを行う。
公式APIリファレンス
https://angular.io/docs/ts/latest/api/core/testing/index/ComponentFixture-class.html#!%23detectChanges-anchor
detectChangesについては、以下がわかりやすい。
日本語訳:Angular 2 Change Detection Explained - Qiita
以下の記事の言葉をお借りすれば、
change Detectionとはモデルの変更を検知し、UIに反映することである
とのこと
引用元 日本語訳:Angular 2 Change Detection Explained - Qiita
fixture.detectChanges()を呼ぶことで、テストからAngilarにchange detection が行われたことを伝える。
Angularで外部のtemplateを読み込む
外部のtemplateやcssファイルを使用するときは、ちょっとした処理が必要なようだ。
But the Angular template compiler must read the external files from the file system before it can create a component instance. That's an asynchronous activity.
引用元: https://angular.io/docs/ts/latest/guide/testing.html#!%23component-fixture
(意訳)
Angularでは、Componentのインスタンスを生成する前に、Angular template compilerが外部ファイルを読み込む必要があり、これは非同期な活動である。
⇒
つまり、Componentのインスタンスを生成する前にAngular template compilerが外部ファイルを読み込めるような処理をbeforeEachの中で行う必要があるわけである。
TestBed.compileComponents
公式APIリファレンス
まず、テスティングモジュールで設定されたComponentを非同期でコンパイルする。
上記のcompileComponentsが完了したら、外部のファイルや、cssファイルはinlinedされていて、
TestBed.createComponentは、同期的にComponentのインスタンスを生成することができる。
参考: https://angular.io/docs/ts/latest/guide/testing.html#!%23component-fixture
templateやcssを外部ファイルにするときは、必ず「Componentのインスタンスを生成する」前に「外部ファイルを読み込む」ように非同期、同期をうまく使う。(コードの記述はこの順番でなくても構わない)
テスト実行画面
以下のコマンドをコマンドライン上で打つとテストランナーであるkarmaによってブラウザが立ち上がり、テスト結果がブラウザ上に表示される。
ng test
参考にさせていただいたサイト
公式
https://angular.io/docs/ts/latest/guide/testing.html#!%23component-fixture
jasmine公式
https://jasmine.github.io/2.0/introduction.html
jasmineわかりやすい記事
Jasmine使い方メモ - Qiita
Jasmine spec覚え書き - Qiita
karmaのわかりやすい記事
step by stepで始めるKarma - Qiita
karma公式
Karma - Spectacular Test Runner for Javascript
公式APIリファレンス
https://angular.io/docs/ts/latest/api/core/testing/index/TestBed-class.html
https://angular.io/docs/js/latest/api/core/testing/index/TestBed-class.html
https://angular.io/docs/ts/latest/api/core/testing/index/ComponentFixture-class.html
https://angular.io/docs/ts/latest/api/core/index/DebugElement-class.html
https://angular.io/docs/ts/latest/api/platform-browser/index/By-class.html
https://angular.io/docs/ts/latest/api/core/testing/index/ComponentFixture-class.html#!%23detectChanges-anchor
detectChangesについて
日本語訳:Angular 2 Change Detection Explained - Qiita
※ ブログでも同一の記事を投稿している。
Angular4(Angular2~)のユニットテスト【Angularのユニットテストの基本とComponentの簡単なテスト編】