SHOEISHA iD

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

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

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

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

ブラウザーを自動で操作し動作確認できる、「Angular」のe2eテスト

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

e2eテストでブラウザーを操作する方法

 リスト2で利用したbrowserやelementを使って、Webブラウザーの操作や結果の取得を行う方法を、図5のサンプル(P002-basic)で説明します。このWebページには、テキストボックス、ボタンと2個のラベルがあり、テキストボックスの入力内容は、データバインディングで上のラベル(メッセージ1)にすぐに反映されます。そのあとでボタンを押すと、下のラベル(メッセージ2)にもテキストボックスの入力内容が反映されます。

図5 e2eテストでテストするサンプルWebページ(P002-basic)
図5 e2eテストでテストするサンプルWebページ(P002-basic)

 このWebページに対するe2eテストのコードはリスト3です。ここでは、Webブラウザーの操作方法が直感的にわかるように、PageObject(app.po.ts)は利用せず、すべてのテスト処理をテストコードに直接記述しています。

[リスト3]図5のWebページに対応するe2eテスト(P002-basic/e2e/src/app.e2e-spec.ts)
import { browser, element, by } from 'protractor'; // インポート ...(1)
describe('P002-basic', () => {
  it('e2eテストの動作確認', () => { // ...(2)
    // URLを指定してWebページを表示 ...(3)
    browser.get('/');
    // ページ内のHTML要素を取得 ...(4)
    const message1 = element(by.css('#message1'));  // CSSを指定できる
    const message2 = element(by.id('message2'));    // IDを直接指定できる
    const textBox1 = element(by.id('textBox1'));
    const button1 = element(by.id('button1'));
    // ページの初期条件 ...(5)
    expect(message1.getText()).toEqual('');
    expect(message2.getText()).toEqual('初期メッセージ');
    // テキストボックスに文字を入力 ...(6)
    textBox1.clear();
    textBox1.sendKeys('テキストボックスに文字を入力');
    // message1はすぐ更新されるが、message2はまだ更新されない ...(7)
    expect(message1.getText()).toEqual('テキストボックスに文字を入力');
    expect(message2.getText()).toEqual('初期メッセージ');
    // ボタンをクリック ...(8)
    button1.click();
    // ボタンクリックで、message2も更新される ...(9)
    expect(message1.getText()).toEqual('テキストボックスに文字を入力');
    expect(message2.getText()).toEqual('テキストボックスに文字を入力');
  });
});

 まず、Protractorがテスト用に提供する変数browser、element、byをインポートします(1)。テストの処理は(2)以降です。

 テストでは、最初に(3)のbrowser.getメソッドに相対URL('/')を指定して、テスト対象のWebページを表示させます。browserはWebブラウザーに関連する操作を行えるオブジェクトです。

 次に(4)で、ページ内のHTML要素を取得します。elementはHTML要素を取得するメソッドで、条件を指定するby.cssメソッド(CSS記述を指定)またはby.idメソッド(HTML要素のIDを指定)と組み合わせて、HTML要素を取得できます。

 (5)では、取得したHTML要素のgetTextメソッドで要素の内容(テキスト)を取得して、想定値と比較しています。

 (6)は、テキストボックスに文字を入力する処理です。テキストボックスの要素に対して、clearメソッドで入力値を削除してから、sendKeysメソッドで文字を入力します。ここでは入力後、(7)でラベルの値を想定値と比較しています。

 (8)のようにclickメソッドを実行すると、その要素をクリックできます。ここではクリック後、(9)でラベルの値を想定値と比較しています。

 このように、Protractorの機能を利用すると、人間が操作するのと同じようにブラウザーを操作して、e2eテストを実行できます。

PageObjectでテストの再利用性を高める

 リスト3では、ページ遷移や要素の取得・操作など、Webページの内容に強く依存する処理を、テストのファイルに直接記述していました。この場合、Webページのレイアウトなどを変更すると、テストの記述も修正する必要があります。

 こういった問題に対して、Webページの内容に強く依存する処理を別のクラスに切り出す「PageObject」と呼ばれる考え方が有効です。Angularのプロジェクトでは、PageObjectのファイルがデフォルトで生成されます。

 図5のWebページに対応するPageObjectは、リスト4のように実装できます。リスト3でテストコードに記述されていたページ遷移やテキストの取得・入力、ボタンのクリックといった処理を実装します。

[リスト4]図5のWebページに対応するPageObject(P003-page-object/e2e/src/app.po.ts)
export class AppPage {
  // ページを表示
  navigateTo() {
    return browser.get('/');
  }
  // メッセージ1を取得
  getMessage1() {
    return element(by.css('#message1')).getText();
  }
  // メッセージ2を取得
  getMessage2() {
    return element(by.id('message2')).getText();
  }
  // テキストボックスに入力
  inputToTextbox(input:string) {
    const textBox1 = element(by.id('textBox1'));
    textBox1.clear();
    textBox1.sendKeys(input);
  }
  // ボタンをクリック
  clickButton1() {
    element(by.id('button1')).click();
  }
}

 このPageObjectを利用すると、リスト3のテストはリスト5のように書き換えられます。

[リスト5]リスト4のPageObjectを利用して記述したe2eテスト(P003-page-object/e2e/src/app.e2e-spec.ts)
describe('P003-page-object', () => {
  let page: AppPage;      // PageObject ...(1)
  beforeEach(() => {
    page = new AppPage(); // PageObjectを初期化 ...(2)
  });
  it('e2eテストの動作確認', () => {
    // ページ遷移
    page.navigateTo();
    // ページの初期条件
    expect(page.getMessage1()).toEqual('');
    expect(page.getMessage2()).toEqual('初期メッセージ');
    // テキストボックスに文字を入力
    page.inputToTextbox('テキストボックスに文字を入力');
    // message1はすぐ更新されるが、message2はまだ更新されない
    expect(page.getMessage1()).toEqual('テキストボックスに文字を入力');
    expect(page.getMessage2()).toEqual('初期メッセージ');
    // ボタンをクリック
    page.clickButton1()
    // ボタンクリックで、message2も更新される
    expect(page.getMessage1()).toEqual('テキストボックスに文字を入力');
    expect(page.getMessage2()).toEqual('テキストボックスに文字を入力');
  });
});

 PageObjectのAppPage(1)を、beforeEachで生成して(2)、テストで利用します。リスト3とリスト5は同じ内容のテストですが、Webページ固有の処理がPageObjectに切り出されていることに注目してください。

 PageObjectを利用すると、Webページの内容に依存する処理をテストのコードから分離できるため、ページのレイアウトの変更があってもPageObjectだけを修正すればよく、テストのコードはそのまま利用できます。また、PageObjectを使いまわして、別のテストを行うこともできます。

次のページ
複数コンポーネントのWebページをe2eテストでまとめて確認

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

次世代Webアプリケーションフレームワーク「Angular」の活用連載記事一覧

もっと読む

この記事の著者

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

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/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本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/11013 2018/08/22 14:00

イベント

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

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

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

メールバックナンバー