Angularをインストールすると、ユニットテストを行うための「Jasmine」がデフォルトで導入されています。
こいつを使えばコードでユニットテストができるので皆ハッピーになれます。
ってことで今回は、AngularでJasmineを使ってユニットテストをやってみマッスル。
僕のプロフィールはこちら
まず、Jasmineって何て読むん?
「ジャスミン」です。
Jasmineを使うにあたって
まあ、さすがに何かアプリがないとあれなので、少し前に僕が作ったアプリを実験用として使ってみます。
■GitHub
URL:「https://github.com/izumin0401/pessimistic-weather」
↑今回の記事内容も反映されているので見てみてね。
AngularでのJasmineの使い方
デフォルトでJasmineが組み込まれているので、実行自体も簡単です。
1 |
ng test |
Jasmineを実行するのは上記コマンドをルートで叩くだけです。
Jasmineで実行されるテストファイル
テスト対象となるファイルは「…spec.ts」というファイルです。
このファイルは「ng g c」コマンドでコンポーネントを作成すると自動的にできるファイルです。
このファイルに記載されているテストがJasmineによってすべて実行されます。
実際にJasmineを使ってみた
僕の作ったアプリは説明の都合上、specファイルは「app.component.spec.ts」のみにしています。
この後の説明で「main.component.spec.ts」が追加されているので、GitHub上にはspecファイルが2ファイルあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import { TestBed, async } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule ], declarations: [ AppComponent ], }).compileComponents(); })); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.debugElement.componentInstance; expect(app).toBeTruthy(); }); }); |
「app.component.spec.ts」ファイルです。
デフォルトから不要なテストケースを削除した状態です。
「コンポーネントがちゃんとできているか?」のテストだけが記載されています。
1 |
ng test |
Jasmineを実行します。
実行すると、デバック用のブラウザが立ち上がり、テスト結果が表示されます。
今回は「1 specs, 0 failures」とあるので、「テストケースが1件、成功1件、失敗0件」となります。
Jasmineでユニットテストを追加してみる
僕の作ったアプリではメイン画面が存在するので、メイン画面コンポーネントのテストコードを追記してみたいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
<h1 class="header">悲観的な天気予報</h1> <mat-tab-group mat-align-tabs="center"> <mat-tab label="通常"> <div class="container mat-elevation-z8"> <table mat-table [dataSource]="dataSource"> <ng-container matColumnDef="time"> <th mat-header-cell *matHeaderCellDef>時刻</th> <td mat-cell *matCellDef="let element">{{ element.time | time }}</td> </ng-container> <ng-container matColumnDef="temp"> <th mat-header-cell *matHeaderCellDef>気温</th> <td mat-cell *matCellDef="let element">{{ element.temp | temp }}</td> </ng-container> <ng-container matColumnDef="clouds"> <th mat-header-cell *matHeaderCellDef>曇り度</th> <td mat-cell *matCellDef="let element"> {{ element.clouds | clouds }} </td> </ng-container> <ng-container matColumnDef="icon"> <th mat-header-cell *matHeaderCellDef>天気</th> <td mat-cell *matCellDef="let element"> <img src="{{ element.icon }}"> </td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table> </div> </mat-tab> <mat-tab label="悲観"> <div class="container mat-elevation-z8"> <table mat-table [dataSource]="pessimisticDataSource"> <ng-container matColumnDef="time"> <th mat-header-cell *matHeaderCellDef>時刻</th> <td mat-cell *matCellDef="let element">{{ element.time | time }}</td> </ng-container> <ng-container matColumnDef="temp"> <th mat-header-cell *matHeaderCellDef>気温</th> <td mat-cell *matCellDef="let element">{{ element.temp | temp }}</td> </ng-container> <ng-container matColumnDef="clouds"> <th mat-header-cell *matHeaderCellDef>曇り度</th> <td mat-cell *matCellDef="let element"> {{ element.clouds | clouds }} </td> </ng-container> <ng-container matColumnDef="icon"> <th mat-header-cell *matHeaderCellDef>天気</th> <td mat-cell *matCellDef="let element"> <img src="{{ element.icon }}"> </td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table> </div> </mat-tab> <mat-tab label="ルール"> <ul> <li>天気予報は「東京の天気」を表示する</li> <li>天気予報は「5日分3時間毎」表示する</li> <li>「通常タブ」は通常の天気予報を表示する</li> <li>「悲観タブ」は悲観的な天気予報を表示する</li> <li>「悲観タブ」では、曇り度が20%以上の場合、雨とする(降水確率取得できなかったのでやむを得ず)</li> </ul> </mat-tab> </mat-tab-group> <footer class="footer"> © 2019 Copyright: <a href="https://traveler0401.com/" target="_blank" class="footera">Traveler</a> </footer> |
まずHTMLっすね。
汚いコードですが勘弁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MainComponent } from './main.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MaterialModule } from 'src/app/module/material.module'; import { PipeModule } from 'src/app/module/pipe.module'; import { SharedModule } from 'src/app/module/shared.module'; describe('MainComponent', () => { let component: MainComponent; let fixture: ComponentFixture<MainComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ MainComponent ], imports: [ BrowserAnimationsModule, MaterialModule, PipeModule, SharedModule, ], }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(MainComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); it('should render title in a h1 tag', () => { fixture = TestBed.createComponent(MainComponent); fixture.detectChanges(); const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('h1').textContent).toContain('悲観的な天気予報'); }); }); |
元々「main.component.spec.ts」はなかったのでファイルを追加しています。
で、テストはと言うと、「コンポーネントがちゃんとできているか?」のテストと「h1タグの文言が正しいか?」のテストの2ケースになります。
※「MainComponent」を生成するにあたり、必要なモジュールを読み込まないとテストに失敗するので「imports」の箇所で読み込んでいます。
Jasmineを再実行してみる
再実行すると結果は上記のようになります。
「3 specs, 0 failures」なので、3ケースともテストに成功しています。
まあそりゃそうだろレベルのテスト。
見たら分かるんですが、「どのコンポーネント」の「どのテスト」が成功したかが分かるようになっています。便利。
今回はここまで!!
おすすめ書籍
僕はAngularの勉強をするのに以下の書籍を購入しました。おすすめですよ!
まとめ
今回は使ってみるだけで終わっちゃいました。
次は、Jasmineの構文についてまとめたいところですが眠いので寝ます。
さよなら。