/var/log/messages

May 26, 2018 - 5 minute read - Comments - Angular

Testing Classes & Pipes

以下なドキュメント機械翻訳控えを投入。

Testing Classes & Pipes

Learning Objectives

  • クラスのインスタンスを単体テストする方法。

Sample class & test suite

あなたが知る必要があること、クラスをテストする方法、ユニットテストの旅を始めます。

Tip Angularのすべては、コンポーネント、ディレクティブ、パイプなどのクラスのインスタンスです。 だから、基本クラスをテストする方法が分かれば、すべてをテストできます。

AuthServiceという単純なクラスがあるとしましょう。これは、Angulars DIフレームワークに提供したいものですが、テストする方法には関係しません。

Listing 1. app/auth.service.ts

export class AuthService {
  isAuthenticated(): boolean {
    return !!localStorage.getItem('token');
  }
}

isAuthenticatedという1つの関数があり、ブラウザlocalStorageにトークンが格納されている場合はtrueを返します。

このクラスをテストするために、auth.service.tsファイルの隣にあるauth.service.spec.tsというテストファイルを作成します。

Listing 2. app/auth.service.spec.ts

import {AuthService} from './auth.service';  // 1.

describe('Service: Auth', () => {  // 2.
});
  1. まず、テストを実行するAuthServiceクラスをインポートします。
  2. 個々のテスト仕様をすべて保持するテストスイート機能の記述を追加します。

Setup & teardown

beforeEach関数とafterEach関数を使用してインスタンスを設定してクリーンアップするため、AuthServiceの新しいインスタンスに対してテスト仕様を実行する必要があります。

Listing 3. app/auth.service.spec.ts

describe('Service: Auth', () => {
  let service: AuthService;

  beforeEach(() => {  // 1.
    service = new AuthService();
  });

  afterEach(() => {  // 2.
    service = null;
    localStorage.removeItem('token');
  });
});
  1. 各テスト仕様を実行する前に、AuthServiceの新しいインスタンスを作成し、サービス変数に格納します。
  2. 各テスト仕様が終了した後、私たちはサービスを無効にし、localStorageに保存したトークンもすべて削除します。

Creating test specs

ここで、いくつかのテスト仕様を作成します。作成する最初の仕様は、トークンがあるときにisAuthenticated関数がtrueを返すかどうかを確認する必要があります。

it('should return true from isAuthenticated when there is a token', () => {  // 1.
  localStorage.setItem('token', '1234');  // 2.
  expect(service.isAuthenticated()).toBeTruthy();  // 3.
});
  1. 私たちは it 関数に私たちがテストしているものの人間が読める記述を渡します。 これはテストレポートに示されており、どの機能が動作していないのかを簡単に理解できます。
  2. 私たちは、私たちが望む効果を引き起こすはずのローカルストレージにいくつかの仕様のみのデータを設定しました。
  3. service.isAuthenticated()関数がtrueに解決する何かを返すという期待をテストします。

また、トークンが存在しない場合に関数がfalseを返す必要がある場合、逆の場合をテストします。

it('should return false from isAuthenticated when there is no token', () => {
  expect(service.isAuthenticated()).toBeFalsy();
});

この関数では、afterEach関数でトークンをクリアすることを確認しているので、トークンは設定されていません。

今、service.isAuthenticated()関数がfalseに解決する何かを返すとの期待をテストします。

Running the tests

テストを実行するには、単にブラウザでHTMLファイルを開きます。単にプランカリンクをクリックし、ツールバーのrunを押すことができます。

class suite pass

Pipes

パイプはAngularのはるかに単純な部分ですが、1つの関数を持つクラスとして実装できるため、これまでに得られた知識だけでなく、ジャスミンだけでテストすることができます。

DefaultPipeというパイプを作成したパイプのセクションでは、このパイプを使用してテンプレートの変数のデフォルト値を提供することができます。

{{ image | default:"http://example.com/default-image.png" }}

このパイプのコードは次のようになっています:

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'default'
})
export class DefaultPipe implements PipeTransform {

  transform(value: string, fallback: string, forceHttps: boolean = false): string {
    let image = "";
    if (value) {
      image = value;
    } else {
      image = fallback;
    }
    if (forceHttps) {
      if (image.indexOf("https") == -1) {
        image = image.replace("http", "https");
      }
    }
    return image;
  }
}

開始テストスイートファイルは次のようになります:

describe('Pipe: Default', () => {
  let pipe: DefaultPipe;

  beforeEach(() => {
    pipe = new DefaultPipe();
  });
});

セットアップ関数では、パイプクラスのインスタンスを作成します。

パイプクラスにはtransformと呼ばれる関数が1つあります。パイプをテストするには、入力と出力を渡してこの関数をテストするだけです。

私たちの最初のテスト仕様では、パイプが入力を受け取らない場合、次のようにデフォルト値を返します:

it('providing no value returns fallback', () => {
  expect(pipe.transform('', 'http://place-hold.it/300')).toBe('http://place-hold.it/300');
});

変換関数への入力として空文字列を渡すので、2番目の引数が返されます。

パイプをテストするためにそれほど多くのことはありません。変換関数のさまざまな入力と予想される出力をチェックするだけです。

注 このテスト仕様ファイルをテストPlunkerで実行するには、それをspec_files配列のテスト仕様ファイルのリストに追加してください。

Tip パイプにコンストラクタに依存関係を注入する必要がある場合は、このセクションで後述する Angular TestBed を使用する方がよいでしょう。

Summary

それは本当に、単純なジャスミン仕様ファイルで何も必要ない分離されたクラスをテストすることができます。複雑なものは必要ありません。

Angularのすべてがクラスとして表現されているので、ここでは止めることができます。ほとんどのツールは、すでにディレクティブ、コンポーネント、パイプなどのテストを記述しています。

しかし、私たちのコードはしばしば他のコードを動作させる必要があります。 だから、自然に分離されておらず、依存関係が必要なコードのための分離されたテストを書く方法は、次の講義の話題です。

Listing

Listing 4. auth.service.ts

export class AuthService {
  isAuthenticated(): boolean {
    return !!localStorage.getItem('token');
  }
}

Listing 5. auth.service.spec.ts

import {AuthService} from './auth.service';

describe('Service: Auth', () => {

  let service: AuthService;

  beforeEach(() => {
    service = new AuthService();
  });

  afterEach(() => {
    service = null;
    localStorage.removeItem('token');
  });

  it('should return true from isAuthenticated when there is a token', () => {
    localStorage.setItem('token', '1234');
    expect(service.isAuthenticated()).toBeTruthy();
  });

  it('should return false from isAuthenticated when there is no token', () => {
    expect(service.isAuthenticated()).toBeFalsy();
  });

});

Listing 6. default.pipe.ts

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'default'
})
export class DefaultPipe implements PipeTransform {

  transform(value: string, fallback: string, forceHttps: boolean = false): string {
    let image = "";
    if (value) {
      image = value;
    } else {
      image = fallback;
    }
    if (forceHttps) {
      if (image.indexOf("https") == -1) {
        image = image.replace("http", "https");
      }
    }
    return image;
  }
}

Listing 7. default.pipe.spec.ts

/* tslint:disable:no-unused-variable */

import {DefaultPipe} from './default.pipe';

describe('Pipe: Default', () => {
  let pipe: DefaultPipe;

  beforeEach(() => {
    pipe = new DefaultPipe();
  });

  it('providing no value returns fallback', () => {
    expect(pipe.transform('', 'http://place-hold.it/300')).toBe('http://place-hold.it/300');
  });

  it('providing a value returns value', () => {
    expect(pipe.transform('http://place-hold.it/300', 'fallback')).toBe('http://place-hold.it/300');
  });

  it('asking for https returns https', () => {
    expect(pipe.transform('', 'http://place-hold.it/300', true)).toBe('https://place-hold.it/300');
  });
});

Jasmine and Karma Amazon Kindle Unlimited の領収

comments powered by Disqus