カテゴリー
プログラミング

Protractor で AngularJS を e2e テストする

はじめに

前回は AngularJS チュートリアルの Step2 にあるテストを実行ました。今回は Step3 にある、e2e テストを実行してみたいと思います。

e2e テスト実行環境について、はじめに簡単に構成を整理します。Webdriver Manager は e2e テストで使用されるブラウザを管理します (Protractor からの指示により、Angular アプリの URL を取得して実行)。プロジェクトとは独立して動作します。Protractor はテストコードに従い、Webdriver Manager に接続してブラウザを操作しながらテストを実行します(テストコードに従い、Webdriver Manager に指示を出しながらテストを実行)。デフォルトで Jasmine フレームワークを使う設定になっていますので、karma を使った単体テストと同様の書き方ができます。あと、テスト対象のアプリに HTTP(S) でアクセスできるよう、Webサーバーにアップロードしたり、簡易 Web サーバーを起動したりする必要があります。本投稿では Python の SimpleHTTPServer モジュールを使用して、プロジェクトのベースディレクトリで簡易 Web サーバーを起動することにします。

3 つのコンポーネントが出てきましたが、下記のような構成を想定しています。

  • CentOS 上のターミナルで常時起動しておく… Webdriver Manager、簡易 Web サーバー
  • 手元の開発環境から SSH 経由のターミナルなどで都度実行する … Protractor

大まかな流れは下記のとおりです。

  • Webdriver Manager と Protractor のインストール
  • Webdriver Manager 起動
  • 簡易 Web サーバー起動
  • e2e テストコード作成
  • e2e テスト実行

Webdriver Manager と Protractor のインストール

Webdriver Manager インストール

ユーザーエリアの node_modules に npm でインストールします。webdriver-manager コマンドがインストールされますが、パスが通っていないため、使いやすくなるようエイリアスとして登録して適用します。

$ npm install protractor
$ echo 'alias webdriver-manager="${HOME}/node_modules/protractor/bin/webdriver-manager"' >> ~/.bashrc
$ source ~/.bashrc

Java インストール

Webdriver Manager は Selenium Server Standalone を起動します。これには Java が必要です。Java はディストリビューションのデフォルト (OpenJDK) または Oracle Java のどちらかをインストールしておく必要があります。

$ sudo yum install java

 Java をインストールしていないと、Webdriver Manager 起動時に下記のようなエラーが発生しました。

seleniumProcess.pid: 3508

events.js:72
        throw er; // Unhandled 'error' event
              ^
Error: spawn ENOENT
    at errnoException (child_process.js:1011:11)
    at Process.ChildProcess._handle.onexit (child_process.js:802:34)

Webdriver Manager 初期化

webdriver-manager の初回起動前に、1回だけ下記コマンドを実行して環境を初期化します。ここで自動的に Webdriver がインストールされます。

$ webdriver-manager update

Webdriver Manager 起動

Webdriver Manager 用のターミナルを 1 つ用意して、下記コマンドを実行します。Webdriver Manager のアクセスログやエラーなどがターミナルに出力されます。

簡易 Web サーバー起動

e2e テスト対象となるアプリに、HTTP でアクセスできるよう、簡易 Web サーバーを起動します。簡易 Web サーバー用のターミナルを 1 つ用意して、下記コマンドを実行します。http://localhost:8000/app/index.html でテスト対象のアプリにアクセスできるようになります。

$ cd ~/work/angular-tutorial/step03
$ python -m SimpleHTTPServer

e2e テストコード作成

前回の投稿をコピーした構成で進めます。下記コマンドを実行して、step03 を作成してください。

$ cp -r ~/work/angularjs-tutorial/step02/ ~/work/angularjs-tutorial/step03/
$ mkdir ~/work/angularjs-tutorial/step03/test/e2e
$ cd ~/work/angularjs-tutorial/step03/
$ touch protractor.conf.js
$ touch test/e2e/scenariosSpec.js

結果、下記のようなディレクトリ・ファイル構成が出来上がっているはずです。

~/work/angularjs-tutorial/step03/  ← プロジェクトのベースディレクトリ
   app/
     js/
       angular.min.js
       controllers.js
     index.html            ← 今回の実装対象
   test/
     e2e/
       scenariosSpec.js    ← e2e テストコード
     js/
       angular-mocks.js
     unit/
       controllersSpec.js
   karma.conf.js
   protractor.conf.js      ← Protractor 用設定ファイル

まだテストを書く前ですが、最低限の実装だけしてしまいます。

app/index.html

<!doctype html>
<html lang="en" ng-app="phonecatApp">
<head>
  <meta charset="utf-8">
  <title>My PhoneCat App</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
  <script src="js/angular.min.js"></script>
  <script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
  
</body>
</html>

e2e テストコードの作成

下記の内容でテストコードを作成します。

test/e2e/scenariosSpec.js

describe('PhoneCat App', function () {

  describe('Phone list view', function () {

    beforeEach(function () {
      browser.get('http://localhost:8000/app/index.html');
    });

    it('should filter the phone list as a user types into the search box', function () {

      var phoneList = element.all(by.repeater('phone in phones'));
      var query = element(by.model('query'));

      expect(phoneList.count()).toBe(3);

      query.sendKeys('nexus');
      expect(phoneList.count()).toBe(1);

      query.clear();
      query.sendKeys('motorola');
      expect(phoneList.count()).toBe(2);
    });
  });
});

Protractor 設定ファイルの作成

Webdriver Manager の接続先 URL、Protractor が実行するテストコードなどを指定します。

protractor.conf.js

exports.config = {
  // Settings for Webdriver Manager
  seleniumAddress: 'http://localhost:4444/wd/hub',
  capabilities: {
    browserName: 'chrome'
  },

  // Settings for e2e Tests.
  specs: ['test/e2e/*Spec.js']
};

e2e テスト実行

Protractor は karma のように、ソースコードを監視して、変更を検出するとテストが実行されるわけではありません。e2e テストを開始する都度、実行する必要があります。

初回の e2e テスト開始

$ cd ~/work/angular-tutorial/step03
$ protractor protractor.conf.js

e2e テストではブラウザを Protractor (Selenium) で実行します。もし CentOS6 でテストを実行する場合は、Webdriver が必要とするライブラリ libstdc++ に新しいバージョンを要求して、下記のようなエラーが発生する場合があります。エラーが発生したら、ライブラリを新しいものに入れ替える必要があります。ライブラリを更新する手順をこちらの投稿にまとめましたので、参考にしてください。

/usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by <some software name>) 

実装がまだですので、下記のようにテストは FAIL します。テスト結果のサマリーは上から 3 行目の「F」です。今回はテストが 1 つしかありませんので、1 つだけ表示されています。





Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
F

Failures:

  1) PhoneCat App - Phone list view - should filter the phone list as a user types into the search box
   Message:
     Expected 0 to be 3.
   Stacktrace:
     Error: Failed expectation
    at [object Object]. (/home/tester/work/angularjs-tutorial/step03/test/e2e/scenariosSpec.js:15:33)

  2) PhoneCat App - Phone list view - should filter the phone list as a user types into the search box
   Message:
     NoSuchElementError: No element found using locator: by.model("query")
   Stacktrace:
     NoSuchElementError: No element found using locator: by.model("query")
    at Array.forEach (native)
Error
    at [object Object]. (/home/tester/work/angularjs-tutorial/step03/test/e2e/scenariosSpec.js:17:13)
From: Task: Asynchronous test function: it()
Error
    at [object Object]. (/home/tester/work/angularjs-tutorial/step03/test/e2e/scenariosSpec.js:10:5)
    at [object Object]. (/home/tester/work/angularjs-tutorial/step03/test/e2e/scenariosSpec.js:3:3)
    at Object. (/home/tester/work/angularjs-tutorial/step03/test/e2e/scenariosSpec.js:1:63)

Finished in 2.153 seconds
1 test, 2 assertions, 2 failures

[launcher] 0 instance(s) of WebDriver still running
[launcher] chrome #1 failed 2 test(s)
[launcher] overall: 2 failed spec(s)
[launcher] Process exited with error code 1

ソースコード修正

下記のように、body タグの中身を実装します。

app/index.html

<!doctype html>
<html lang="en" ng-app="phonecatApp">
<head>
  <meta charset="utf-8">
  <title>My PhoneCat App</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
  <script src="js/angular.min.js"></script>
  <script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
  
  <div class="container-fluid">
    <div class="row">
      <div class="col-md-2">
        <!--Sidebar content-->

        Search: <input ng-model="query">

      </div>
      <div class="col-md-10">
        <!--Body content-->

        <ul class="phones">
          <li ng-repeat="phone in phones | filter:query">
            {{phone.name}}
            <p>{{phone.snippet}}</p>
          </li>
        </ul>

      </div>
    </div>
  </div>

</body>
</html>

実装後の e2e テスト開始

$ protractor protractor.conf.js

テストコードを満足する実装になっていると、下記のとおり PASS となります。
 テスト結果のサマリー (上から 3 行目) は PASS を表す「.」が表示されています。

Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
.

Finished in 3.276 seconds
1 test, 3 assertions, 0 failures

[launcher] 0 instance(s) of WebDriver still running
[launcher] chrome #1 passed

おわりに

今回も、テストを実際に実行できるようにする手順に焦点をあてました。テスト実行環境が CentOS6 ということもあり、主にライブラリ周りで手間取ることもありますが、常時オンラインの CI 環境として活用すると便利なのではないでしょうか。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください