はじめに
前回は 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 環境として活用すると便利なのではないでしょうか。