Web無料相談会2018冬
Web無料相談会2018冬
2015.11.05

ProtractorでE2E(end to end)テストを自動化しよう

いなば

こんにちは、フロントエンドエンジニアのいなばです。
先日iPhone5からiPhone6sへ機種変更をしました。
サイズがでかいのは慣れませんが指紋認証すごいですね。

さて、今回はProtractorを使ってE2Eテストを実行できる環境をつくってみました。
Windowsをお使いの方は動かなかったらごめんなさい。

今回作ってみたE2Eテスト環境のリポジトリはこちらになります。

早速テストを書いてみましょう……の前に

すぐE2Eテストを書いてみたい方は、導入まで読み飛ばしてください。

E2Eテストとは

E2Eというのは、「End to End(エンドツーエンド)」の略で、「両端で」「端から端まで」という意味の言葉だそうです。

E2Eテストは、Webサイトやアプリケーションの「開始から終了まで」が、期待通り動いているかをテストします。

みなさん、Webサイトなどのリリース前には複数のブラウザや端末を操作して、目視で動作確認をしていますよね。今回はProtractorを使ってE2Eテストを自動化できるようにしたいと思います。

Protractorとは

Protractor   end to end testing for AngularJS

http://angular.github.io/protractor

ProtractorはAngularJS製アプリケーションのE2Eテストをするために開発された、E2Eテストフレームワークです。
AngularJSを使っていないWebサイトでもE2Eテストを実行することができます。

ProtractorはWebDriverJS(Node.jsからSeleniumを利用するためのライブラリ)を利用して実際にブラウザを操作し、テストを実行します。

導入

Node.jsのインストールをしていない場合はインストールをしてください。
インストールはこちらからできます。

https://nodejs.org/en/

Java(jdk)の確認 / インストール

本稿を執筆時ではProtractor(Seleniumサーバー)を動作させるためにJava7(jdk7)以上がインストールされている必要がありました。

まずJavaのバージョンを調べます。

$ java -version

Javaがインストールされていない、もしくは1.7以下の場合はJava(jdk)のインストールを行ってください。

Java8のインストールはこちらからできます。
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

こんな表示がされていればOKです。

$ javac -version
java version "1.8.0_65"

Protractorのセットアップ

リポジトリをクローンしてきたら、package.jsonと同じ階層に移動して依存モジュールをインストールしましょう。

$ npm install --unsafe-perm

これでProtractorのセットアップは完了です。

Protractorをセットアップする際は、Protractorに同梱されているwebdriver-managerのupdateが必要です。しかし、package.jsonのscriptsにpostinstallを指定しておくと、npm installしたあとにpostinstallで指定したコマンドを自動で実行してくれるということを知り、今回早速使ってみました。

▼package.json

"scripts": {
    "postinstall": "./node_modules/protractor/bin/webdriver-manager update",
    "start": "./node_modules/protractor/bin/webdriver-manager start",
    "test": "protractor protractor.conf.js"
  },

テストを動かしてみる

以下のコマンドでテストが実行されます。

$ npm run test

ブラウザが立ち上がったと思いきや、すぐに閉じませんでしたか?
今の一瞬で、テストに書かれた操作がブラウザ上で実際に実行されました。

今回はサンプルとして、当社のフロントエンドエンジニアメンバーのページをテスト対象としたテストコードを書いてみました。

テストの最後に、おまけでSelenium WebDriverでスクリーンショットを撮ってみています。

StrongestCSSDesign

▼spec/gotoSpec.js

var fs = require('fs');

function writeScreenShot(data, filename) {
    var stream = fs.createWriteStream('./capture/' + filename);
    stream.write(new Buffer(data, 'base64'));
    stream.end();
}

describe('LIG メンバーページ', function() {

    beforeEach(function() {
        var width = 1440;
        var height = 900;
        browser.driver.manage().window().setSize(width, height);

        // 非Angularのページの場合に必要
        browser.ignoreSynchronization = true;
    });

    it('ページタイトル', function() {
        browser.get('https://liginc.co.jp/member/member_detail?user=h.goto');
        expect(browser.getTitle()).toEqual('後藤 寛一|メンバー | 株式会社LIG');
    });

    it('名前', function() {
        var header = element(by.css('.b_member_header--jp'));
        header.getText().then(function(res){
            //console.log(res);
            expect(res).toBe('後藤 寛一');
        });

        var subHeader = element(by.css('.b_member_header--en'));
        expect(subHeader.getText()).toBe('Hirokazu Goto');
    });

    it('紹介文', function() {
        var text = element(by.css('.b_member_photo--text'));
        expect(text.getText()).toBe('フロントエンドエンジニアのおじいちゃんと言います。本当は24歳です。よろしくお願いします。');
    });

    it('ページ遷移のテスト', function() {
        element.all(by.css('.navi_triple--item')).get(0).click();
        expect(browser.getCurrentUrl()).toBe('https://liginc.co.jp/member/member_detail?user=horiguchi');
    });

    it('スクリーンショットを撮る', function() {
        browser.takeScreenshot().then(function (png) {
            writeScreenShot(png, 'StrongestCSSDesign.png');
        });
    })
});

Protractorに用意されている多くのメソッドがPromiseを返します。

https://github.com/angular/protractor/blob/master/docs/control-flow.md#promises-and-the-control-flow

ProtractorではJasmineのexpect関数にPromiseオブジェクトが渡された場合にPromiseの解決を待ってから値の検証をしてくれるようです。

header.getText().then(function(res){
    //console.log(res);
    expect(res).toBe('後藤 寛一');
});

// 同じ結果
expect(header.getText()).toBe('後藤 寛一');

ProtractorのAPI

今回のサンプルでは、

  • ブラウザのウィンドウサイズの制御
  • ページタイトルの取得
  • ページ内のDOM要素にアクセスしてテキストの比較
  • 取得したDOM要素のクリックしてページ遷移先の確認

などをざっくりと試してみましたが、ProtractorにはまだまだたくさんのAPIが用意されています。

本当にたくさんあるので、詳しくは公式のドキュメントを参照してください。

http://angular.github.io/protractor/#/api

configの設定例

今回の環境では以下のようにしています。
seleniumServerJarを指定することで protractor protractor.conf.jsを実行したときに、Seleniumサーバーの起動&終了を自動で行ってくれます。

selenium-server-standalone-*.jarのような指定の仕方ができないので、適宜書き換えてください。

▼protractor.conf.js

exports.config = {
    //seleniumAddress: 'http://localhost:4444/wd/hub',
    seleniumServerJar: './node_modules/protractor/selenium/selenium-server-standalone-2.47.1.jar',
    specs: ['spec/**/*Spec.js']
    //,
    //capabilities: {}, // ブラウザ単体でテストする場合の設定
    //multiCapabilities: [  // 複数ブラウザでテストする場合の設定
    //    {
    //        'browserName': 'chrome'
    //    },
    //    {
    //        'browserName': 'firefox'
    //    }
    //]
};

さらに詳しい設定方法は公式のドキュメントを参照してください。

https://github.com/angular/protractor/blob/master/docs/referenceConf.js

終わりに

いかがでしたでしょうか。
前回はユニットテストの環境をご紹介しましたが、プログラムコードの実装をあまり気にしないで動作のテストができるので、E2Eテストの方が敷居がやや低いのかなという感じがしました。

参考資料

AngularJS用テストフレームワーク「Protractor」チュートリアル日本語訳
http://qiita.com/weed/items/30098f7be2f753580f63

こわくない Protractor
http://qiita.com/shuhei/items/6973fe694d29a193f224

Protractorでキャプチャを撮る
http://memo.goodpatch.co/2014/08/codepatch-003/