SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

次世代Webアプリケーションフレームワーク「Angular」の活用

ソフトウェアの品質を高めて作業効率も上げる、Angularの自動単体テスト

次世代Webアプリケーションフレームワーク「Angular」の活用 第13回

  • X ポスト
  • このエントリーをはてなブックマークに追加

サービスの単体テスト(2)

ひな型をベースに単体テストを記述

 リスト3のひな型をもとにitメソッドを増やして、テストを記述できます。サンプルコードではリスト4のような実装を複数記述しています。さまざまな引数のパターンでgetGreetメソッドを実行して、戻り値greetが想定通りになっているか、expect(greet).toEqualメソッドで確認します。

[リスト4]追加記述した単体テストコード(P001-basic/src/app/greet.service.spec.ts)
it('日本語:朝1', inject([GreetService], (service: GreetService) => {
  const greet = service.getGreet('太郎', new Date(2005, 3, 1, 0, 0, 0, 0), 'ja-JP');
  expect(greet).toEqual('おはようございます, 太郎');
}));

テストの実行

 Angular CLIで「ng test」コマンドを実行すると、単体テストが実行されて、実行結果がChromeブラウザーに表示されます。図3はすべてのテストが成功した表示です。

図3 単体テストの実行結果(P001-basic)
図3 単体テストの実行結果(P001-basic)

 コマンドを実行中の状態で、GreetServiceや単体テストの実装を編集すると、テストが自動的に再実行されます。図4は、編集でテストが失敗するようになった表示です。

図4 実装とテストが整合しなくなり、単体テストが失敗した表示(P001-basic)
図4 実装とテストが整合しなくなり、単体テストが失敗した表示(P001-basic)

 以上の通り「ng test」コマンドを利用すると、リアルタイムで単体テストの結果を確認しながら実装を進められます。

HTTP通信を含むサービスをモックでテスト

 ネットワーク通信でAPIからデータを取得するといった、外部との通信を伴うサービスをテストする場合、通信処理をモック(本物のクラスの代わりに、テスト用の値を返却するクラス)で置き換えると便利です。通信処理を含む図5のサンプル(P002-mock)で、モックの利用法を説明します。テキストボックスに入力された検索語でWikipediaを検索して、検索結果のJSONを画面に表示します。

図5 APIでWikipediaを検索するサンプル(P002-mock)
図5 APIでWikipediaを検索するサンプル(P002-mock)

 Wikipediaを検索するWebApiServiceをリスト5に示します。

[リスト5]Wikipediaを検索するWebApiServiceサービス(P002-mock/src/app/web-api.service.ts)
@Injectable()
export class WebApiService {
  // 依存性注入でHttpClientを受け取る ...(1)
  constructor(private myClient: HttpClient) { }
  // Wikipediaを検索 ...(2)
  searchWikipedia(query: string) {
    return this.myClient.jsonp( // JSONPで通信実行 ...(3)
      'http://ja.wikipedia.org/w/api.php?format=json&action=query&list=search&srsearch='
      + encodeURIComponent(query), 'callback')
      .pipe(
        map(p => p['query']['search'])// 必要なノードを抽出 ...(4)
      );
  }
}

 (1)のコンストラクターで、HTTP通信を行うHttpClientのインスタンスを依存性注入で受け取り、(2)のsearchWikipediaメソッドでHttpClientを利用してAPIを実行します。Webページとオリジン(ドメインとポート番号)が異なるURLのAPIを呼び出すため、(3)のようにjsonpメソッドでJSONP通信をさせます。API結果が格納されたJavaScriptオブジェクトpから、(4)で必要なノードを抽出します。

 コンポーネント側では、サービスから返却されたRxJSのObservableを購読(Subscribe)して(4)で抽出したノードを取り出し、画面に反映します。実装の詳細はサンプルコードを参照してください。

 

HttpClientのモックを作成して利用

 モックを利用してリスト5のサービスをテストするコードを、リスト6に示します。

[リスト6]リスト5のサービスに対する単体テスト(P002-mock/src/app/web-api.service.spec.ts)
// テストデータ ...(1)
const expectedValue = [
  { 'title': 'test1' },
(略)
];
// API結果のデータ構造と合致するように調整 ...(2)
const dummyResponse = { 'query': { 'search': expectedValue } };

// テスト初期化処理 ...(3)
beforeEach(() => {
  // jsonpメソッドで固定値を返却するHttpClientのモックを作成 ...(4)
  const httpClientSpy = jasmine.createSpyObj('HttpClient', ['jsonp']);
  httpClientSpy.jsonp.and.returnValue(asyncData(dummyResponse));
  // (4)のモックを利用するサービスを生成 ...(5)
  const testService = new WebApiService(httpClientSpy);
  TestBed.configureTestingModule({
    providers: [
      // テスト対象サービスを(5)のサービスに入れ替え ...(6)
      { provide: WebApiService, useValue: testService }
    ]
  });
});
// テスト処理 ...(7)
it('モックでHttpClientを模擬', inject([WebApiService], (service: WebApiService) => {
  service.searchWikipedia('someQuery')
    .subscribe(v => {
      expect(v).toEqual(expectedValue); // テストデータが返却される ...(8)
    });
}));

 (1)がモックのテストデータです。HttpClientのモックから返却される値は、WikipediaのAPIで取得できるデータと構造を合わせて、(2)のように調整します。

 (3)がテストの初期化処理です。(4)のjasmine.createSpyObjメソッドで、HttpClientのモック(Spyオブジェクト)を作成します。メソッドの第1引数にモックの名前、第2引数にモックを作るメソッド名配列(ここでは「jsonp」1つだけ)を指定します。作成したモックhttpClientSpyのjsonp.and.returnValueメソッドで、jsonpメソッド実行時に(2)のdummyResponseを含むObservableが返却されるように指定します。asyncDataは、購読されたときに指定データを返却するObservableを生成する処理です。詳細はサンプルコードを参照してください。

 (5)で、本物のHttpClientの代わりに(4)のモックをコンストラクターに与えてWebApiServiceを生成します。TestBed.configureTestingModuleメソッド内で、providersを(6)のように記述すると、テスト実行時に(5)のtestServiceが依存性注入されるようになります。「useValue」は、依存性注入されるオブジェクトを指定するオプションです。

 テスト処理は(7)です。依存性注入で取得されるWebApiService型の変数serviceは、実は(5)のtestServiceなので、searchWikipediaメソッドの内部でHttpClientのモックからデータを取得して、最終的に取得される値はexpectedValueと一致します(8)。

次のページ
コンポーネントの単体テスト

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
次世代Webアプリケーションフレームワーク「Angular」の活用連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト  吉川 英一(ヨシカワ エイイチ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10838 2018/05/28 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング