Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
35
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Not Angularな環境でProtractorを使ってES2015(ES6), ES7なE2Eテストコードを書いてみた

追記

注意
サンプルコードなので省いていますがコードではawaitとしてPromiseのコールバックを隠ぺいしています。
実際にはPromiseresolveではなくrejectを返すこともあるので、実際のコードではtry-catchを使ってちゃんとエラーを捕捉しましょう。

async func() {
  try {
    let t = await funcReturnPromise();
  } catch(err) {
    console.log(err);
  }
}

はじめに

ProtractorはAunglarのためのように使われてますが、Yahoo Mailでの自動テストに書いてあるようにReact+Flux環境でもテスト(機能テスト、スモークテスト、インテグレーションテスト)に使われているということで試してみました。

以下の方針でコード例を記述します。

  • ES2015を使ってモダンにテストコードを書く
  • mocha + power-assertなテスト環境
  • E2EテストはPageObjectパターンを利用

環境

  • Mac OS X Yosemite
  • Node 4.1.1

準備

ディレクトリ構造

$ tree -I node_modules
.
├── package.json
├── protractor.conf.js
└── spec
    ├── test1
    │   ├── BingPageObject.js
    │   └── bing_spec.js
    └── test2
        ├── GooglePageObject.js
        └── google_spec.js

設定ファイル等

今回使うライブラリはpackage.jsonの通りです。

package.json
{
  "name": "example_for_protractor",
  "version": "1.0.0",
  "description": "Example for Protractor",
  "license": "MIT",
  "devDependencies": {
    "babel": "^5.8.23",
    "babel-plugin-espower": "^1.0.0",
    "mocha": "^2.3.3",
    "power-assert": "^1.1.0",
    "protractor": "^2.4.0"
  },
  "scripts": {
    "postinstall": "webdriver-manager update"
  }
}

使ったライブラリでミソなのはpower-assert-js/babel-plugin-espowerです。
これは以下のprotractor.conf.jsで活用します。

protractor.conf.js
// Babel Require Hookを通してpower-assertを使ったES2015コードを変換する
require("babel/register")({
  only: /spec/,
  plugins: ['babel-plugin-espower'],
  extensions: ['.es6', '.js']
});

exports.config = {
  // specの実行前に、protractorが準備できたら一度だけ呼ばれる関数
  onPrepare: function () {
    browser.ignoreSynchronization = true;
  },

  directConnect: true,

  capabilities: {
    'browserName': 'chrome'
  },

  framework: 'mocha',

  suites: {
    test1: './spec/test1/*_spec.js',
    test2: './spec/test2/*_spec.js'
  },

  mochaOpts: {
    ui: 'bdd',
    reporter: "spec",
    slow: 3000,
    enableTimeouts: false
  }
};

ProtractorでES2015なテストコードを扱う場合はprotractor.conf.jsを起点に呼ばれるのでrequire("babel/register")を仕込んでおきます。
またさらにpower-assertも使いたい場合はbabel-plugin-espowerもフックさせると良いです。
こうしておくことで $(npm bin)/protractor protractor.conf.js だけでES2015とpower-assertの変換を掛ける必要がなくなるので書いたコードをすぐ実行できるようになります。

そしてonPrepare内の browser.ignoreSynchronization = true; を書くことでAngularの処理が完了するまで待たない(Not Angular環境でもProtractorが使える)ということになります。

テストコード

例としてとりあえず書いたコードを以下にあげます。
なんだこのテストは、というものもあるかもしれませんがそこは例ということで。

spec/test1/BingPageObject.js
"use strict";
export default class BingPageObject {
    constructor(url) {
        this.url = 'https://www.bing.com/';
    }

    async getPage() {
        await this.navigateTo(this.url);
    }

    async navigateTo(url) {
        await browser.get(url);
    }

    async getTitle() {
        let title = await browser.getTitle();
        return title;
    }

    async typeQuery(query) {
        let textbox = await $('#sb_form_q');
        return await textbox.sendKeys(query);
    }

    async search(query) {
        let submit = await $('#sb_form_go');
        await this.typeQuery(query);
        return submit.click();
    }

    async getQuery() {
        let textbox = await $('#sb_form_q');
        return await textbox.getAttribute('value');
    }

    async getAlgo() {
        let algo = await $$('#b_results > li').filter(async(elem) => {
           let cls = await elem.getAttribute('class');
           return cls === 'b_algo';
        });

        return await algo.map(async(elem) => {
            let title = await elem.$('h2').getText();
            return title;
        });
    }
}
bing_spec.js
"use strict";

import assert from "power-assert";

import BingPageObject from "./BingPageObject"

describe('Bing.com', () => {
    let page;
    let query = '天気';

    before(async() => {
        page = new BingPageObject();
        page.getPage();
        await page.search(query);
    });

    it('検索窓に入力したクエリが表示されている', async() => {
         let text = await page.getQuery();
         assert(text === query);
    });

    it('アルゴが表示されている', async() => {
        let algo = await page.getAlgo();
        assert(algo.length === 10);
    });
});

実行結果

$ $(npm bin)/protractor protractor.conf.js --suite test1
Using ChromeDriver directly...
[launcher] Running 1 instances of WebDriver


  Bing.com
    ✓ 検索窓に入力したクエリが表示されている
    ✓ アルゴが表示されている


  2 passing (3s)

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

ちょっとテストコードを失敗するようにしてみると

$ $(npm bin)/protractor protractor.conf.js --suite test1
Using ChromeDriver directly...
[launcher] Running 1 instances of WebDriver


  Bing.com
    1) 検索窓に入力したクエリが表示されている
    ✓ アルゴが表示されている


  1 passing (3s)
  1 failing

  1) Bing.com 検索窓に入力したクエリが表示されている:

      AssertionError:   # spec/test1/bing_spec.js:19

  assert(text === 'hoge')
         |    |          
         |    false      
         "天気"          

  --- [string] 'hoge'
  +++ [string] text
  @@ -1,4 +1,2 @@
  -hoge
  +天気


      + expected - actual

      -false
      +true

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

いい感じにpower-assertも動いているようですね!

まとめ

もともとこの内容は息抜きのつもりでいて、ProtractorとMochaやProtractorとBabelといった内容は見つかったのですが、さらにpower-assertを合わせたり、Angular環境でない場合はどうすればという疑問から試して書き起こしてみました。

余談ですがProtractor以外にも、個人的には(少し試しただけですが)E2Eテストとしてgroupon/testiumも悪くないかと思います。

参考資料

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
35
Help us understand the problem. What are the problem?