WebdriverIOとは
- Selenium Webdriverを利用し、ブラウザのE2E(End to End)テストをJavaScript(Node.js)で記述してブラウザ上で操作してテストするライブラリです。
- JavaScriptで普及しているテストフレームワーク(Mocha, Jasmine, Cucumber)で記述できます。
V4.0から同期(synchronous)で記述可能に!
非同期での記述方法(sync:false
の場合)
describe('asynchronus test',()=>{
it('Promise',()=>{
browser.url('/')
browser.getTitle().then( title =>{
assert( /WebdriverIO/.test(title) )
})
})
})
-
Promise
に対してthen
のブロックでassert
します。
generator
での記述方法(V3.0で記述できた)
describe('asynchronus test',()=>{
it('Generator',function* (){
browser.url('/')
const title = yield browser.getTitle()
assert( /WebdriverIO/.test(title) )
})
})
- テストコードを
generator
で記述function* (){}
、yield
で戻り値を取得できます。Promiseの時よりコールバックがひとつ減ります。 - V4.0では同期で記述できるようになったため、
generator
のサポートは無くなりました。https://github.com/webdriverio/webdriverio/issues/1159
V4.0でサポートした同期(synchronus)の記述方法
describe('synchronus test',()=>{
it('get title',()=>{
browser.url('/')
assert( /WebdriverIO/.test( browser.getTitle() ) )
})
})
- テストコードを直感的に記述できる !!!
実際に試してみる
準備
mocha
+ power-assert
で記述したテストコードをPhantomJS
で実行するサンプルを用意したので、以下のように試してみましょう。
また、babelも組み込んでいるので、ES2015で記述できます。
$ git clone git@github.com:yoshi6jp/wdio4-sample.git
$ cd wdio4-sample/
$ npm install
実行結果
$ npm run test
:
[phantomjs #0-0] Running: phantomjs
[phantomjs #0-0]
[phantomjs #0-0] sync: test
[phantomjs #0-0] ✓ get title
[phantomjs #0-0]
[phantomjs #0-0]
[phantomjs #0-0] 1 passing (6s)
[phantomjs #0-0]
- こんな感じに表示されるはずです。(spec reporterを利用しています。)
非同期(sync:false
)のサンプルコードの実行結果
$ npm run async
:
[phantomjs #0-0]
[phantomjs #0-0] sync: false
[phantomjs #0-0] ✓ Promise
[phantomjs #0-0]
[phantomjs #0-0]
[phantomjs #0-0] 1 passing (2s)
[phantomjs #0-0]
- wdio.conf.jsで
sync:false
と設定すると従来のPromiseのインタフェースが利用できます。
同期型のテストコードに非同期処理を組み込む場合
テストシナリオによっては、非同期処理を組み込みたい場合があります。
例えば、テストの延長で
- 表示内容をローカルファイルに書き込み
- WebAPIでたのWebサービスとRESTで連携
などです。
ファイル書き込みの場合は、fs.writeFileSyncのような同期型APIを利用すれば解決できますが、非同期型APIしか提供されていないライブラリを利用するユースケースもあります。
以下のようなPromiseインタフェースを持った処理をテストシナリオに組み込む例について解説します。
/**
* @returns {Promise}
*/
const asyncFn = ()=> new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log("--- 1 ---")
resolve()
},1000)
})
Promiseで実装
単純にPromise
のthen
のブロックに後の処理を記述します。
describe('asynchronus',()=>{
it('Promise', ()=>{
return asyncFn().then( () => {
console.log("--- 2 ---")
browser.url('/')
assert( /WebdriverIO/.test( browser.getTitle() ) )
console.log("--- 3 ---")
})
})
})
実行結果
$ npm run sync
:
--- 1 ---
--- 2 ---
:
[phantomjs #0-0] Running: phantomjs
[phantomjs #0-0]
[phantomjs #0-0] sync: true
[phantomjs #0-0]
[phantomjs #0-0] asynchronus
[phantomjs #0-0] 1) Promise
[phantomjs #0-0]
[phantomjs #0-0]
[phantomjs #0-0] 1 failing (3s)
[phantomjs #0-0]
[phantomjs #0-0] 1) asynchronus Promise:
assert(/WebdriverIO/.test(browser.getTitle()))
| | |
| | Object{defer:#defer#,promise
:
false
-
then
のブロックの内部だとbrowser.getTitle()
がPromiseを返すようになり、エラーになってしまいます。
Fiberで実装
it
のブロックの内部は、Fiberの空間になっているので、Fiberのインタフェースで記述します。
import Fiber from 'fibers'
:
describe('asynchronus',()=>{
it('Fiber',()=>{
const fiber = Fiber.current
asyncFn().then(()=>
fiber.run()
})
Fiber.yield()
console.log("--- 2 ---")
browser.url('/')
assert( /WebdriverIO/.test( browser.getTitle() ) )
console.log("--- 3 ---")
})
})
- 非同期処理を開始した後、一旦
Fiber.yield()
で処理を中断し、非同期処理が完了した後にfiber.run()
で処理を再開しています。
実行結果
$ npm run sync
:
--- 1 ---
--- 2 ---
--- 3 ---
:
[phantomjs #0-0] Running: phantomjs
[phantomjs #0-0]
[phantomjs #0-0] sync: true
[phantomjs #0-0]
[phantomjs #0-0] asynchronus
[phantomjs #0-0] ✓ Fiber
[phantomjs #0-0]
[phantomjs #0-0]
[phantomjs #0-0] 1 passing (7s)
[phantomjs #0-0]
- 実行結果も期待通りの動作になりました。
browser.callで実装
WebdriverIOが提供しているAPIのbrowser.callを利用することで、もっとシンプルに実装できます。
describe('asynchronus',()=>{
it('browser.call',()=>{
browser.call(()=> asyncFn())
console.log("--- 2 ---")
browser.url('/')
assert( /WebdriverIO/.test( browser.getTitle() ) )
console.log("--- 3 ---")
})
})
-
browser.call
は、引数としてPromise
を戻り値とする関数を渡す必要があります。
実行結果
$ npm run sync
:
--- 1 ---
--- 2 ---
--- 3 ---
:
[phantomjs #0-0] Running: phantomjs
[phantomjs #0-0]
[phantomjs #0-0] sync: true
[phantomjs #0-0]
[phantomjs #0-0] asynchronus
[phantomjs #0-0] ✓ browser.call
[phantomjs #0-0]
[phantomjs #0-0]
[phantomjs #0-0] 1 passing (8s)
その他の便利な機能
$, $$
V4.4から追加されたAPIです。それぞれbrowser.element
browser.elements
のショートカットとしてグローバルスコープに定義されています。
// ボタンをクリック
$("button.btn").click()
このように$
(document.querySelector), $$
(document.querySelectorAll)と同じように、テストコードを記述することができます。
Repl
V4.5からReplが追加されました。Replから直接ブラウザを操作できるので、APIやセレクターの動作を確認しながら、テストコードが記述できます。
準備
ヘッドレスブラウザ(PhantomJS)だと動作が分かりにくいので、selenium-standalone
を使ってインストールします。
$ npm install selenium-standalone
$ ./node_modules/.bin/selenium-standalone install
----------
selenium-standalone installation starting
:
selenium-standalone installation finished
-----
インストールが完了したらselenium
を起動します。
$ ./node_modules/.bin/selenium-standalone start
別のターミナルを開いてReplを起動します。同時にブラウザ(Chrome)も起動します。
$ ./node_modules/.bin/wdio repl chrome
[21:09:49] DEBUG Queue has stopped!
[21:09:49] DEBUG You can now go into the browser or use the command line as REPL
[21:09:49] DEBUG (To exit, press ^C again or type .exit)
> browser.url("http://webdriver.io")
> $$("nav a")[2].click()
後は、ReplでAPIを試すことができます。実際にブラウザで動作することが確認できると思います。
まとめ
-
Webdriver.IO
を使うとJavaScriptで同期的にテストを記述できます。 -
mocha
power-assert
$
$$
など、ユニットテストを書くのと同じような感覚でE2Eテストを記述できます。