Ionicアプリにユニットテストを導入する

![1_EKbZZOcx2IfRAe4bYF_eeQ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537165/a5644582-d99b-3d66-c213-c4a98d632b4a.png)


Ionicアプリケーションに自動テストの導入するための方法を残します。コンポーネントテストの導入まで記載しています。Angularに標準で入っているKarma + jasmineの情報が多いのでこれを使っていきます。

###[Karma](https://karma-runner.github.io/latest/index.html)
テストフレームワークを動かします。Jenkins等と連携することでCI環境を構築できます。
設定ファイルの`karma.conf.js`にテストブラウザなどを記述する。
QUnitが良ければ、設定を変更すれば使えるはずです。

###[jasmine](https://jasmine.github.io/)
テストフレームワーク。これの記述方法に従ってテストコードを作成していきます。
書き方については公式ドキュメントやQiita記事を参照。

ぱっと見て通常のテストフレームワークと大きな違いはないように見えます。
テスト実行前に`beforeEach`メソッドが動き、DOMの値やメソッドの返却値と比較検証するという感じです。

[Qiita - Jasmine使い方メモ](https://qiita.com/opengl-8080/items/cf3acafda9756f4b04c9)

##テストの準備
Angularプロジェクトには初めからKarmaとjasmineは初めからはいっているので、特に準備は必要ありません。

`ionic start`でプロジェクトを作成していきます。
シンプルでわかりやすいので`tabs`のスターターを使っていきます。

プロジェクトのルートで`npm test`を実行するとテストを実行開始します。
スタータープロジェクトにあるいくつかのテストが実行されています。

##コンポーネントのテスト
あるメンバ変数のときに想定通りの値がビューに反映されているか確認します。
テストがわかりやすいようにテンプレートに変数を追加しています。

```tab1.page.html
<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <!-- テンプレートにビュー変数を追加 -->
      <ion-title size="large">{{title}}</ion-title>
    </ion-toolbar>
  </ion-header>
  <app-explore-container name="Tab 1 page"></app-explore-container>
</ion-content>
```

```tab1.page.ts
export class Tab1Page {

  // 追加
  public title = 'Tab 1';
  constructor() {}

  // メソッドを追加
  public returnTitle() {
    return 'return ' + this.title;
  }
}
```

次にテストコードを書いていきます。
コンポーネントのメンバ変数に設定して、ビューの変更を実行、その後にjasmineのテストメソッドを使って検証するという流れです。
ポイントは自動でビューへ反映されるのではなく、`detectChanges`を使って開発者の任意のタイミングでビューへ反映します（`ComponentFixtureAutoDetect`で自動反映にすることもできる）。

```tab1.page.spec.ts
describe('Tab1Page', () => {
  let component: Tab1Page;
  let fixture: ComponentFixture<Tab1Page>;

  beforeEach(async(() => {

    // テスト用モジュールの上でテスト対象のコンポーネントをテストする
    TestBed.configureTestingModule({
      declarations: [Tab1Page],
      imports: [IonicModule.forRoot(), ExploreContainerComponentModule]
    }).compileComponents();

    // テスト対象のコンポーネントをインスタンス化する。
    // テスト対象のコンポーネントとやりとりするのに必要。
    fixture = TestBed.createComponent(Tab1Page);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }));

  it('title 初期値', () => {

    // セレクターで検証したい要素を抽出する
    const element = fixture.debugElement.query(By.css('ion-content ion-title'));
    // jasmineのテストメソッドで比較する
    expect(element.nativeElement.innerText).toEqual('Tab 1');
    // コンポーネントのメソッドを呼び出して検証する
    expect(component.returnTitle()).toEqual('return Tab 1');
  });

  // titleの値を変更した後の検証
  it('title = サンプル', () => {
    component.title = 'サンプル';
    // データバインディングを実行します。
    fixture.detectChanges();

    // titleに'サンプル'を設定し、データバインディングした状態で検証する
    const element = fixture.debugElement.query(By.css('ion-content ion-title'));
    expect(element.nativeElement.innerText).toEqual('サンプル');
    expect(component.returnTitle()).toEqual('return サンプル');
  });
});
```
これで`npm test`を実行すると追加したテストをパスしていることがわかります。
![スクリーンショット 2020-06-29 1.01.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537165/fe32121a-9ba0-0c6a-6cfe-113b48d24d1a.png)

##おわり
テストコードを記述することでAngularをより深く知れるような気がします。
今回はコンポーネントテストの導入について書きましたが、次回はサービスクラスのテストについて書きたいと思います。

今回使ったコードは[こちら](https://github.com/yunity29/ionic-test-sample/blob/master/src/app/tab1/tab1.page.spec.ts)です。