LoginSignup
16
12

More than 5 years have passed since last update.

WebdriverIOを使ってJSで同期的にE2Eテストを書く

Posted at

WebdriverIOとは

  • Selenium Webdriverを利用し、ブラウザのE2E(End to End)テストをJavaScript(Node.js)で記述してブラウザ上で操作してテストするライブラリです。
  • JavaScriptで普及しているテストフレームワーク(Mocha, Jasmine, Cucumber)で記述できます。

V4.0から同期(synchronous)で記述可能に!

  • V3.0では、generatorを使ってyieldで記述できました。
  • V4.0では、fiberを組み込み同期(synchronus)で記述できるようになりました。

非同期での記述方法(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で実装

単純にPromisethenのブロックに後の処理を記述します。

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テストを記述できます。
16
12
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
12