Blog

ブログ

Angularのテストコードを学ぶ

2022.11.08 公開
Momoka_Ide
Momoka_Ide プログラマ

テストコード、活用していますか?
この記事では、テストコードを書いたことがない人向けに、やっていること、なんとなくわかった!と思ってもらえることを目標にやさしく紹介していきます。

テストコードとは何か

公式ページにある、テストコードの解説を読んでいくと

Angularアプリケーションをテストすると、アプリケーションが期待どおりに動作していることを確認できます。

と書いてあります。要するに、
単体テストのようなテストを、コードで実現できるのがテストコードです。

どこで書くのか

$ ng g c hoge

コマンドでコンポーネントを作成すると、以下のファイルが生成されます。

この中の、spec.tsで終わるファイル名のファイルを使ってテストコードを書いていきます。

どう実行するのか

$ ng test

angularでプロジェクトを生成すると、package.jsonにデフォルトでtestというコマンドが設定されています。
これを実行することで、画像のような画面が起動し、spec.tsに書かれたテストコードを実行した結果が確認できます。
○ specs, ○ failures, ……
ここでテストケースの件数と、失敗した件数を確認できます。

テストコードを読み解く

例として、プロジェクトを生成した直後のapp.componentのspec.tsがどんなことが書いてあるのかを確認していきましょう。

大きく分けて、2つのブロックがあります。

  1. beforeEach()
  2. it()

順に説明していきます

beforeEach

beforeEach(async ()=>{
      await TestBed.configureTestingModule({
           imports:[ RouterTestingModule ],
           declarations:[ AppComponent ],
      }).compileComponents();
});

beforeEachは、②のブロックにあるit()が呼び出されるたびに実行される、初期化処理のようなものです。
ここでは、app.componentに必要なモジュールを読み込んでいます。

例えばapp.component.htmlに、HogeComponentが埋め込まれていたり、入力フォームが実装されているのであれば、declarationsにHogeComponent、importsにFormsModuleが追加される感じです。

it

itに書いてあることがいわゆる一つのテストケースに当たります。

1つ目のit

it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent); <- AppComponentを生成
    const app = fixture.componentInstance;
    expect(app).toBeTruthy();
  });

これを日本語的に解釈すると、
it( テストケースの名前 , () => {
テストの内容
expect(どの値が).どうなっていたら成功なのか();
});
というふうに書いてあります。

expect(app).toBeTruthy();

これは、app(AppComponentのインスタンス)がtrueであれば成功である
という意味です。テストケース名通り、AppComponentが正常に生成されていれば成功、というテストです。

2つ目のit

  it(`should have as title 'test-code-app'`, () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.componentInstance;
    expect(app.title).toEqual('test-code-app');
  });

fixture.componentInstanceには、app.component.tsに定義されている変数が含まれており、これを用いて値を比較したり、更新したりすることができます。

app.component.tsでは、プロジェクト作成時、titleという変数が定義されています。

以上を踏まえて、expectでテスト結果がどうであるべきと記載されているのかを確認すると、
app.component.tsのtitleという変数が、’test-code-app’と等しければ成功
と書いてありますね。

3つ目のit

  it('should render title', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('.content span')?.textContent).toContain(
      'test-code-app app is running!'
    );
  });
const compiled = fixture.nativeElement as HTMLElement;

2つめのitではtsファイル側にアクセスしましたが、3つ目のitではhtml側にアクセスしています。

compiled.querySelector('.content span')?.textContent

contentクラスにあるspanという要素を探し、その中のテキストを取得しています

expectの中身がちょっと長いので簡略化すると、
expect(A).toContain(B);
これはAがBを含んでいるなら成功、と書いてあります。つまり、spanのテキストが、「test-code-app app is running!」であれば成功というテストです。

html側で、上記のような変数を含む文章になっているものをテストコードで確認する際、ただTestBed .createComponent()をしただけでは、tsファイルの変数とhtmlの表示が紐づいていないような状態になっています。

fixture.detectChanges();

そのため、上記のコードを実行することで、tsファイル側の変数の値を、html側に反映しています。
もしdetectChanges()を実行せずテストを実行した場合、
「 app is running!」と「test-code-app app is runnning!」を比較してしまい、エラーになってしまう可能性があります。


app.component.spec.tsに書かれたテストコードは以上になります。
expectで使用する比較関数がさまざまなものがありますので、ぜひ調べてみてください。
Jasmine、Matchersとかで検索すると色々見つかると思います😊

まずは関数の実行結果が正しいものであるかをテストするテストコードから、
書くことを慣れていきたいと思います…🖋