AngularJS用テストフレームワーク「Protractor」チュートリアル日本語訳

![protractor-logo-2.png](https://qiita-image-store.s3.amazonaws.com/0/12939/bac0ed6a-cb4b-53f4-fe68-bed513c2b285.png)


Protractorとは、AngularJSアプリケーションのためのエンド・トゥー・エンド（E2E）テスト・フレームワークです。これは[Protractor Tutorial](https://github.com/angular/protractor/blob/master/docs/tutorial.md)の日本語訳です。

チュートリアル
========

これはシンプルなチュートリアルです。内容は、Protractorをセットアップし、テストを走らせ始める方法です。（訳注：protractorは分度器のことです）

訳注：本チュートリアルの[サンプル・リポジトリ](https://github.com/weed/protractor-tutorial_p140828)を作りました。参考になれば幸いです。

準備
-------------

Protractorは[Node.js](http://nodejs.org/)のプログラムです。動かすには、Node.jsをインストールしておかなければなりません。ProtractorはNode.jsについてくる[npm](https://www.npmjs.org/)を使ってダウンロードすることができます。`node --version`と打って、Node.jsのバージョンをチェックして下さい。v0.10.0以降でなければなりません。

デフォルトでは、Protractorは[Jasmine](http://jasmine.github.io/1.3/introduction.html)テスト・フレームワークをテストするインターフェースのために使います。このチュートリアルでは、Jasmineについてあるていど知っているものとして進めます。

このチュートリアルは、ブラウザを制御するため、ローカルのスタンドアローンなSeleniumサーバを使うテストをセットアップします。スタンドアローンのSeleniumサーバーを動かすために[Java Development Kit (JDK)](http://www.oracle.com/technetwork/java/javase/downloads/index.html)が必要になるでしょう。コマンドラインから`java -version`コマンドを走らせてJDKが入っているか調べて下さい。

セットアップ
-----

npmを使ってProtractorをグローバルにインストールして下さい：

    $ npm install -g protractor

これで2つのコマンドライン・ツールがインストールされます。`protractor`と`webdriver-manager`です。`protractor --version`と打ってみて、それが確かに動いているか確認して下さい。

`webdriver-manager`は、動作中のSeleniumサーバのインスタンスを簡単に取得するためのヘルパー・ツールです。必要なバイナリをダウンロードするために使って下さい：

    $ webdriver-manager update

さあ、サーバを起動しましょう：

    $ webdriver-manager start

これでSeleniumサーバーが起動し、a bunch of info logs を出力します。Protractorのテストは、ローカルのブラウザを制御するために、このサーバにリクエストを送ります。チュートリアルのあいだは、このサーバを起動したままにしておいて下さい。`http://localhost:4444/wd/hub`にアクセスすることで、サーバのステータスについての情報を見ることができます。

訳注：サンプルプロジェクトをセットアップの状態にするにはプロジェクトフォルダの中に入って以下のようにします：

    $ git checkout -f setup

サンプルプロジェクトではprotractorをグローバルではなくローカル（プロジェクトフォルダ内）にインストールしました。具体的には、以下のように作りました：

    $ npm init
    $ npm install protractor --save-dev

webdriver-managerの実行は以下のように行いました。

    $ ./node-modules/protractor/bin/webdriver-manager update
    $ ./node-modules/protractor/bin/webdriver-manager start

ステップ 0 - テストを書こう
---------------------

新しいコマンドラインかターミナル・ウィンドウを開き、テストのためのまっさらなフォルダを作って下さい。

Protractorを動かすのに必要なのは、**specファイル**と**設定ファイル**の2つのファイルです。

さあ、とあるAngularJSアプリケーションを表示してそのタイトルをチェックするというシンプルなテストから始めましょう。そのアプリケーションは、「Super Calculator」（[http://juliemr.github.io/protractor-demo/](http://juliemr.github.io/protractor-demo/)）です。

以下のspec.jsをコピーして下さい：

```js:spec.js
describe('AngularJSホームページ', function() {
  it('タイトルを持つ', function() {
    browser.get('http://juliemr.github.io/protractor-demo/');

    expect(browser.getTitle()).toEqual('Super Calculator');
  });
});
```

`describe`と`it`の文法はJasmineフレームワークのものです。`browser`はProtractorによって作られたグローバルで、`browser.get`でページを表示すると言ったブラウザレベルのコマンドのために使われます。

さあ、設定ファイルを作りましょう。以下の内容をconf.jsにコピーして下さい：

```js:conf.js
exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['spec.js']
}
```

この設定ファイルに書かれているのは、Protractorに、どこにあなたのテストファイルがあるか（`specs`）と、あなたのSeleniumサーバと対話するにはどうしたらよいか（`seleniumAddress`）です。他のすべての設定はデフォルトになっています。Chromeがデフォルトのブラウザです。

では、以下のようにテストを走らせましょう：

    $ protractor conf.js

すると、Chromeブラウザのウィンドウが開きCalculatorのページを開き、それからウィンドウが閉じます（これはあっというまです）。テストの出力は`1 tests, 1 assertion, 0 failures`となっているはずです。おめでとう、あなたは最初のProtractorテストを走らせたのです。

訳注：サンプルプロジェクトをステップ0の状態にするにはプロジェクトフォルダの中に入って以下のようにします：

    $ git checkout -f step-0

サンプルプロジェクトではまずpackage.jsonファイルに以下のコードを追加しました。

```json:package.json
  "scripts": {
    "test": "protractor test.js",
    "conf": "protractor conf.js"
  },
```

テストを走らせるには以下のようにしました：

    $ npm run conf

ステップ 1 - 要素を調べる
----------------------------------

さて、ページ上の要素を調べるためにテストを修正しましょう。spec.jsを以下のように変更します：

```js:spec.js
describe('AngularJSホームページ', function() {
  it('1と2を加える', function() {
    browser.get('http://juliemr.github.io/protractor-demo/');
    element(by.model('first')).sendKeys(1);
    element(by.model('second')).sendKeys(2);

    element(by.id('gobutton')).click();

    expect(element(by.binding('latest')).getText()).
        toEqual('5'); // これはまちがい！
  });
});
```

このテストではglobal?（編集リクエスト求む）の`element`と`by`が使われています。これらはProtractorによって作られています。`element`関数はWebページからHTML要素を見つけるために使われます。ElementFinderオブジェクトが返り、それは要素を調べて情報を得るのに使うことができます。このテストでは、2つの`<input>`に数字を打ち込み、`click`によってボタンをクリックし、`getText`によって要素の内容を返すために`sendKeys`を使います。

`element`はLocatorという1つの引数をとります。これは、その要素をどのように見つけるかを記述します。`by`オブジェクトはLocatorをつくります。ここでは、3つのタイプのLocatorを使っています：

  - `by.model('first')`は、`ng-model="first"`の付いた要素を見つけます。もしあなたがCalculatorページのソースを調べれば、これは`<input type=text ng-model="first">`のことだということが見つかることでしょう。
  - `by.id('gobutton')`は、idを与えられた要素を見つけます。これは`<button id="gobutton">`を見つけます。
  - `by.binding('latest')`は、`latest`変数に結びつけられた要素を見つけます。これは`{{latest}}`を含む<span>要素を見つけます。

  [LocatorとElementFinderについて、もっと学ぶ](https://github.com/angular/protractor/blob/master/docs/locators.md).

以下のようにテストを走らせて下さい：

    $ protractor conf.js

あなたは、そのページに2つの数字が入力されるのを見るでしょう。そして、結果が表示されるのを待つでしょう。結果は3で、5ではないので、私たちのテストは失敗します。テストを修正して再度テストを走らせてみて下さい。

訳注：サンプルプロジェクトをステップ1の状態にするにはプロジェクトフォルダの中に入って以下のようにします：

    $ git checkout -f step-1

サンプルプロジェクトでテストを走らせるには以下のようにして下さい：

    $ npm run conf

ステップ 2 - 複数のシナリオを書く
-----------------------------------

これらの2つのテストをいっしょにして、すこし形を整えましょう。spec.jsを以下のように変えます：

```js:spec.js
describe('AngularJSホームページ', function() {
  var firstNumber = element(by.model('first'));
  var secondNumber = element(by.model('second'));
  var goButton = element(by.id('gobutton'));
  var latestResult = element(by.binding('latest'));

  beforeEach(function() {
    browser.get('http://juliemr.github.io/protractor-demo/');
  });

  it('タイトルを持つ', function() {
    expect(browser.getTitle()).toEqual('Super Calculator');
  });

  it('1と2を加える', function() {
    firstNumber.sendKeys(1);
    secondNumber.sendKeys(2);

    goButton.click();

    expect(latestResult.getText()).toEqual('3');
  });

  it('4と6を加える', function() {
    // この部分を自分で書いて下さい
    expect(latestResult.getText()).toEqual('10');
  });
});
```

ここでは、ページを表示する制御を`beforeEach`関数の中に入れました。それはそれぞれの`it`ブロックの前に実行されます。また、一つ目と二つ目の入力のためのElementFinderをいい感じの変数に格納して再利用可能にしました。これらの変数を使って、2番目のテストを自分で書いて下さい。そしてテストを再度走らせ、通ることを確認して下さい。

訳注：サンプルプロジェクトをステップ2の状態にするにはプロジェクトフォルダの中に入って以下のようにします：

    $ git checkout -f step-2

ステップ 3 - 設定を変えてみましょう
-----------------------------------

さて、初歩的なテストをいくつか書いたところで、設定ファイルを見てみることにしましょう。設定ファイルはどのブラウザを使うか、とかどうやってSeleniumサーバに接続するか、といったことを変えることができます。では、ブラウザを変えてみましょう。conf.jsを以下のように変えます：

```js:conf.js
exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['spec.js'],
  capabilities: {
    browserName: 'firefox'
  }
}
```

再度テストを走らせてみて下さい。テストがChromeの代わりにFirefox上で走るはずです。`capabilities`にはテストを実行するブラウザを記述します。すべてのオプションを見るには、[設定ファイル・リファレンス](https://github.com/angular/protractor/blob/master/docs/referenceConf.js)を見て下さい。

訳注：サンプルプロジェクトをステップ3の前半の状態にするにはプロジェクトフォルダの中に入って以下のようにします：

    $ git checkout -f step-3-1

2つ以上のブラウザでテストを走らせることもできます。conf.jsを以下のように変えて下さい：

```js:conf.js
exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['spec.js'],
  multiCapabilities: [{
    browserName: 'firefox'
  }, {
    browserName: 'chrome'
  }]
}
```

もう一度テストを走らせてみて下さい。テストがChromeとFirefox上でsimultaneouslyに実行され、結果がコンソール上に別々に表示されるはずです。

訳注：サンプルプロジェクトをステップ3の後半の状態にするにはプロジェクトフォルダの中に入って以下のようにします：

    $ git checkout -f step3-2

ステップ 4 - 要素のリスト
--------------------------

テスト・ファイルに戻りましょう。設定ファイルは、一つのブラウザだけを使うように変えてもらって結構です。

時折、複数の要素のリストを扱いたくなるでしょう。`element.all`を使うことでこれができます。これは一つのElementArrayFinderを返します。計算機アプリケーションでは、操作のそれぞれは履歴に記録されます。履歴は、そのサイト上で`ng-repeat`を使った表として埋め込まれています。いくつか操作しましょう。そして、履歴に入った操作をテストしましょう。spec.jsを以下のように変えます：

```js:spec.js
describe('AngularJSホームページ', function() {
  var firstNumber = element(by.model('first'));
  var secondNumber = element(by.model('second'));
  var goButton = element(by.id('gobutton'));
  var latestResult = element(by.binding('latest'));
  var history = element.all(by.repeater('result in memory'));

  function add(a, b) {
    firstNumber.sendKeys(a);
    secondNumber.sendKeys(b);
    goButton.click();
  }

  beforeEach(function() {
    browser.get('http://juliemr.github.io/protractor-demo/');
  });

  it('履歴がある', function() {
    add(1, 2);
    add(3, 4);

    expect(history.count()).toEqual(2);

    add(5, 6);

    expect(history.count()).toEqual(0); // これはまちがい！
  });
});
```

ここではいくつかのことをしました。最初に、`add`というヘルパー関数を作りました。`history`変数を追加しました。`element.all`を`by.repeater` Locatorといっしょに使ってElementArrayFinderオブジェクトを取得しました。このテストでは、履歴が想定された長さを持つかどうかを、`count`メソッドを使うことで確かめています。テストを直して2番目の想定が通るようにして下さい。

`ElementArrayFinder`は、`count`の他にもたくさんのメソッドを持っています。Locatorによって見つかった最後の要素のマッチするElementFinderを取得するために`last`を使いましょう。テストを以下のように変えます。

```js
  it('履歴がある', function() {
    add(1, 2);
    add(3, 4);

    expect(history.last().getText()).toContain('1 + 2');
    expect(history.first().getText()).toContain('foo'); // これはまちがい！
  });
```

計算機はいちばん古い結果をいちばん下に表示するため、最後の（1 + 2）の加算は履歴entryの最後になるはずです。`toContain`というJasmineのmatcherを使うことで、その要素テキストが「1 + 2」を含んでいることを検証することができます。要素テキスト全体はタイムスタンプと結果も含みます。

テストを直して、最初の履歴がテキスト「3 + 4」を含むことを正しく想定するようにして下さい。

ElementArrayFinderは他にもJavaScriptの配列のメソッドに似た`each`、`map`、`filter`、`reduce`というメソッドを持っています。[詳細はAPIドキュメントを読んで下さい](https://github.com/angular/protractor/blob/master/docs/api.md#api-elementarrayfinder).

訳注：サンプルプロジェクトをステップ4の状態にするにはプロジェクトフォルダの中に入って以下のようにします：

    $ git checkout -f step-4

この次に読むもの
----------------

これであなたはテストを書く準備ができました。さらに学びたい場合は、ドキュメントを読んで下さい。[目次](https://github.com/angular/protractor/blob/master/docs/toc.md)。