Edited at

Angular4(Angular2~)のユニットテスト【Angularのユニットテストの基本とComponentの簡単なテスト編】

More than 1 year has passed since last update.


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


app.component.ts

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 = 'クリックされたぜ!';
}
}



app.component.html

<h1>

{{title}}
</h1>

<p class="button" (click)="changeH1Element()">ここをクリックして</p>


タイトルがあって、「ここをクリックして」というところを押すと、タイトルが変更される。

なお、今回の目的はAngularのユニットテストの学習のため、CSSなどによる装飾等は一切行わない。


テストクラス


app.component.spec.ts

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の構成に則っているようである。

1. beforeEachで、各テストSpecの処理の前に行う処理を記述する。(場合によってはafterEachで、各テストSpecの処理の後に行う処理を記述する。)

2. 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

ng-karma.png


参考にさせていただいたサイト

公式

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の簡単なテスト編】