Webアプリが正しく動作することを、Webブラウザーを操作して確認する E2E (End-to-End) テスト。テストの記述には様々なプログラミング言語が使えます。
Selenium WebDriver + JavaScript で E2E テストをするやり方が
An Introduction to WebDriver Using the JavaScript Bindings – Tuts+ Code Tutorial
で紹介されています。
この記事は、基本となる WebDriverJS 以外に、7つのクライアントAPIライブラリーを紹介しています。どれも github で公開されていたので、スター数を調べてみました (2014/12/07時点)。
また、テストの際に、期待通りの値になっているかどうかを調べる assert API がライブラリに組み込まれているか、それとも別途 jasmine や mocha, Q といったテストフレームワークが必要かも調べました。
Client API | スター数 | assert | 補足 |
---|---|---|---|
WebDriverJS | N/A | 別途 | W3Cで標準化しているAPIで書く。JavaでSelenium動かしていた人向け。 |
WD.js | 658 | 別途 | builder pattern (fluent interface) で書く。 |
WebDriver.io | 661 | 別途 | builder patternで書く。WD.jsより短く書ける。 |
Testium | 203 | 別途 | CoffeeScript で書く。 |
Leadfoot | 63 | 別途 | Internで使われてる。WD.jsと似たAPI。 |
Nightwatch | 2,386 | 組み込み | 設定ファイルが必要。拡張コマンドは別ファイルに書く。 |
DalekJS | 481 | 組み込み | Selenium Server まで入った全部入り。Webサイトが派手w。 |
Webdriver-sync | 36 | 組み込み | Java APIに準拠。同期型。 |
さらに、WebdriverJS, WD.js, WebdriverIO, Nightwatch, Dalek の5つについて、実際にコードを書いてみました。
お題は「Googleで”webdriver”を検索した時のヒット数を標準出力に表示する」です。assert は使いません。
共通の事前準備
Mac を使います。Web ブラウザーは Chrome です。
Homebrew と Cask で node.js と java をインストールします。
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" $ brew install node $ brew brew install brew-cask $ brew cask install java
さらに Selenium Server と Chrome Driver をインストールします。
$ npm install selenium-standalone chromedriver -g
コードを実行する前に、Selenium Server を起動しておきます。ただし、Dalek の場合は不要です。
$ start-selenium
WebDriverJS
APIはこちら。豊富です。
http://selenium.googlecode.com/git/docs/api/javascript/index.html
インストール
$ npm install selenium-webdriver
コード
webdriverjs.js:
var webdriver = require('selenium-webdriver'); var By = webdriver.By; //WebブラウザーはChrome var driver = new webdriver.Builder(). withCapabilities(webdriver.Capabilities.chrome()). build(); var $ = driver.findElement.bind(driver); //Googleを開く。 driver.get('http://www.google.com'); //検索ボックスにwebdriverと入力する。 $(By.name('q')).sendKeys('webdriver'); //検索ボタンを押す。 $(By.name('btnG')).click(); //ヒット数が表示されるまで待つ。 var timeoutMSec = 2000; driver.wait(webdriver.until.elementLocated(By.id('resultStats')), timeoutMSec) .then(function() { //waitした後はthenでつなぐ $(By.id('resultStats')).getText().then(function(text) { console.log(text); }); }) .then(function() { driver.quit(); });
実行
$ node webdriverjs.js 約 615,000 件 (0.11 秒)
WD.js
sendKeys() など WebDriverJS の名残があります。また、waitForElementByCss()など、APIが若干長めです。
インストール
$ npm install wd
コード
wd.js:
var wd = require('wd'); var browser = wd.promiseChainRemote(); var timeoutMSec = 1000; browser .init({ browserName: 'chrome' }) .get('http://www.google.com') .elementByName('q') .sendKeys('webdriver') .elementByName('btnG') .click() .waitForElementByCss('#resultStats', timeoutMSec) .text(function(err, text) { console.log(text); }) .quit();
実行
$ node wd.js 約 615,000 件 (0.12 秒)
Webdriver.io
今回試した中では、API 名が短くて好きです。
インストール
$ npm install webdriverio
コード
webdriverio.js:
var timeoutMSec = 1000; var webdriverio = require('webdriverio') .remote({ desiredCapabilities: { browserName: 'chrome' } }) .init() .url('http://www.google.com') .setValue('[name="q"]', 'webdriver') .click('[name="btnG"]') .waitFor('#resultStats', timeoutMSec) .getText('#resultStats', function(err, text) { console.log(text); }) .end();
実行
$ node webdriverio.js 約 615,000 件 (0.12 秒)
Nightwatch
runner を使って実行するので、出力が綺麗です。
デフォルトのブラウザーが Firefox で、Chrome を使うためには設定ファイルが必要です。
拡張コマンドを置くためのディレクトリが必要です。
インストール
$ npm install nightwatch -g $ mkdir -p examples/custom-commands
コード
nightwatch.json:
{ "selenium": { "cli_args": { "webdriver.chrome.driver": "/usr/local/bin/chromedriver" } }, "test_settings" : { "default" : { "silent": true, "desiredCapabilities": { "browserName": "chrome" } } } }
nightwatch.js:
module.exports = { "webdriverの検索ヒット数を表示する" : function (browser) { var timeoutMSec = 1000; browser .url("http://www.google.com") .setValue('[name="q"]', 'webdriver') .click('[name="btnK"]') // FirefoxとChromeでは表示されるボタン名が違います。 .waitForElementPresent('#resultStats', timeoutMSec) .getText('#resultStats', function(res) { console.log(res.value); }) .end(); } };
実行
$ nightwatch -t nightwatch.js [Nightwatch] Test Suite ======================= Running: webdriverの検索ヒット数を表示する ✔ Element <#resultStats> was present after 1038 milliseconds. 約 615,000 件 (0.14 秒) OK. 1 total assertions passed. (11.812s)
DalekJS
Selenium server を動かす必要はありません。
インストール
$npm install dalek-cli -g $npm install dalekjs dalek-browser-chrome --save-dev
コード
waitForElementが動作しないので、waitForを使いました(github)。
標準出力への表示はexecute()とlog.message()を組み合わせて使います。
dalek.js:
module.exports = { 'webdriverのヒット数を表示する': function (test) { var timeoutMSec = 10000; test .open("http://www.google.com") .setValue('[name="q"]', 'webdriver') .click('[name="btnG"]') //.waitForElement('#resultStats', timeoutMSec) .waitFor(function() { return Boolean(document.querySelector('#resultStats')); }, [], timeoutMSec) .execute(function() { var result = document.querySelector('#resultStats').innerText; this.data('result', result); }) .log.message(function() { return test.data('result'); }) .done(); } };
実行
引数 -b chrome を与えて Chrome で動かします。
デフォルトではヘッドレスブラウザーの PhantomJS で動かします。
$ dalek dalek.js -b chrome Running tests Running Browser: Google Chrome OS: Mac OS X 10.10.1 x86_64 Browser Version: 39.0.2171.71 RUNNING TEST - "webdriverのヒット数を表示する" ▶ OPEN http://www.google.com ▶ SETVALUE [name="q"] ▶ CLICK [name="btnG"] ▶ WAITFOR ▶ EXECUTE ☁ [USER] MESSAGE: 約 615,000 件 (0.38 秒) ✔ 0 Assertions run ✔ TEST - "webdriverのヒット数を表示する" SUCCEEDED 0/0 assertions passed. Elapsed Time: 4.44 sec
どれを選ぶか
短い API が好きなので、Webdriver.ioかNightwatchですね。すでにテストフレームワークをチームに導入しているかどうかで決める。
(ただ、Nightwatch の対抗馬としてInternも気になります。もうちょっと調べてみよう)