WebDriver+JavaScriptでWebアプリのE2Eテスト

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.ioNightwatchですね。すでにテストフレームワークをチームに導入しているかどうかで決める。

(ただ、Nightwatch の対抗馬としてInternも気になります。もうちょっと調べてみよう)

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中