75
72

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-06-08

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

75
72
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
75
72

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?