追記
注意
サンプルコードなので省いていますがコードではawait
としてPromise
のコールバックを隠ぺいしています。
実際にはPromise
がresolve
ではなく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の通りです。
{
"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
で活用します。
// 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が使える)ということになります。
テストコード
例としてとりあえず書いたコードを以下にあげます。
なんだこのテストは、というものもあるかもしれませんがそこは例ということで。
"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;
});
}
}
"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も悪くないかと思います。
参考資料
- Protractor側の設定とか書き方とか
- Using ES2015 in protractor.conf.js · Issue #2463 · angular/protractor · GitHub
- reactjs - Preprocess e2e tests' files (ES6 styling) for Protractor using webpack - Stack Overflow
- Using Page Objects to Organize Tests - angular/protractor · GitHub
- javascript - What is browser.ignoreSynchronization in protractor? - Stack Overflow
- ProtractorのPromiseをasync/awaitで手軽に扱いたかった