40歳からのキャリアチェンジ

20代はエンジニア・PM、30代はWeb系エンジニア向けのキャリアアドバイザー。40代の今はフリーランスで開発含めて色々やってます。技術ネタとしてはRuby/RailsとJavaScript関連あたり

WebアプリのUI自動化テストでNightwatch.jsを試す

Titaniumで作ってきたCraftBeerFanですが、Alloyで全面的に書きなおしつつこんな感じ↓でiOS7に対応させる作業を先日終えました

f:id:h5y1m141:20140601073727p:plain

ひとまず申請はして、現在Waiting For Reviewになってて、この合間にあまり手を入れてないCraftBeerFanのWebアプリの方を手入れすることに決めました。

スマホアプリ版ではすでに実装してる開栓情報からの検索機能を実装しようかなと思ったけど、UI部分含めた結合テスト(っていうんですかね?)をあまりしっかりしてないので、その辺りも前から気になっていたのでそっちに力を入れることにしました。

UI自動化テストのツールに何を選ぶか?

Titanium Mobileで開発してることもあって、なるべくNode.js界隈の技術に精通してたほうが色々応用聞くと思って最近はNode.jsなツールを選定するように心がけています。

少し前にQiitaに Nightwatch.jsで自動ブラウザテストという投稿があって、Node.jsで書いたテストコードをSeleniumで実行してくれるという割と自分の求めてることが出来そうだったのでこれを試しました

環境構築

Seleniumサーバ

上記記事だと、jarファイルダウンロード・・となってましたが、Macなので、brew 使ったほうがいいかなと思ったので以下のようにしました。

~/selenium
% brew install selenium-server-standalone
Warning: Your Xcode (5.0.2) is outdated
Please update to Xcode 5.1.
Xcode can be updated from the App Store.
==> Downloading http://selenium-release.storage.googleapis.com/2.41/selenium-server-standalone-2.41.0.jar
######################################################################## 100.0%
==> Caveats
To have launchd start selenium-server-standalone at login:
    ln -sfv /usr/local/opt/selenium-server-standalone/*.plist ~/Library/LaunchAgents
Then to load selenium-server-standalone now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.selenium-server-standalone.plist
Or, if you don't want/need launchctl, you can just run:
    selenium-server -p 4444

WARNING: launchctl will fail when run under tmux.
==> Summary
🍺  /usr/local/Cellar/selenium-server-standalone/2.41.0: 4 files, 33M, built in 11 seconds
~/selenium

最初これを見て、エイリアスを・・・と思ったけど、上記brew でインストールした時のターミナルに

To have launchd start selenium-server-standalone at login:
    ln -sfv /usr/local/opt/selenium-server-standalone/*.plist ~/Library/LaunchAgents
Then to load selenium-server-standalone now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.selenium-server-standalone.plist
Or, if you don't want/need launchctl, you can just run:
    selenium-server -p 4444

とあり、Mac起動時にlaunchd経由でselenium-serverを起動させる方法書いてあったので

ln -sfv /usr/local/opt/selenium-server-standalone/*.plist ~/Library/LaunchAgents

とした後に、すぐに起動させたかったので、書いてある通り

launchctl load ~/Library/LaunchAgents/homebrew.mxcl.selenium-server-standalone.plist

としました。

実際に起動してるのかどうか確認するためには、launchctlに、listオプションをつけてあげればよいみたいだったので、そのオプションをつけて実行しつつ、Unixコマンドラインのパイプ機能をつかって、grepで、seleniumが含まれてるものだけ画面表示してみたら以下のようになったのでひとまずOKっぽい

% launchctl list|grep selenium
62199   -       homebrew.mxcl.selenium-server-standalone

Nightwathc.js

すでにnpm install 出来る環境整えてるのでこっちは簡単に作業できました

~//node/nightwatchjs
% sudo npm install -g nightwatch
Password:
npm http GET https://registry.npmjs.org/nightwatch
npm http 200 https://registry.npmjs.org/nightwatch
npm http GET https://registry.npmjs.org/nightwatch/-/nightwatch-0.4.17.tgz
npm http 200 https://registry.npmjs.org/nightwatch/-/nightwatch-0.4.17.tgz
npm http GET https://registry.npmjs.org/ejs
npm http GET https://registry.npmjs.org/mkpath
npm http GET https://registry.npmjs.org/minimatch
npm http GET https://registry.npmjs.org/optimist
npm http 200 https://registry.npmjs.org/mkpath
npm http GET https://registry.npmjs.org/mkpath/-/mkpath-0.1.0.tgz
npm http 200 https://registry.npmjs.org/ejs
npm http 200 https://registry.npmjs.org/minimatch
npm http 200 https://registry.npmjs.org/mkpath/-/mkpath-0.1.0.tgz
npm http GET https://registry.npmjs.org/ejs/-/ejs-1.0.0.tgz
npm http 200 https://registry.npmjs.org/optimist
npm http 200 https://registry.npmjs.org/ejs/-/ejs-1.0.0.tgz
npm http GET https://registry.npmjs.org/lru-cache
npm http GET https://registry.npmjs.org/sigmund
npm http GET https://registry.npmjs.org/wordwrap
npm http GET https://registry.npmjs.org/minimist
npm http 304 https://registry.npmjs.org/sigmund
npm http 304 https://registry.npmjs.org/wordwrap
npm http 200 https://registry.npmjs.org/minimist
npm http 304 https://registry.npmjs.org/lru-cache
/usr/local/bin/nightwatch -> /usr/local/lib/node_modules/nightwatch/bin/nightwatch
nightwatch@0.4.17 /usr/local/lib/node_modules/nightwatch
├── mkpath@0.1.0
├── ejs@1.0.0
├── minimatch@0.2.14 (sigmund@1.0.0, lru-cache@2.5.0)
└── optimist@0.6.1 (wordwrap@0.0.2, minimist@0.0.10)

環境整ったのでNightwatch.jsを試す

上記のQiitaの記事を参考にNightwatch.jsを試そうとしたら、こんなメッセージが出たので、Selenium FirefoxDriverが必要なように見えました

===========================

Running:  Demo test Google
There was an error while executing the Selenium command - enabling the --verbose option might offer more details.
Cannot find firefox binary in PATH. Make sure firefox is installed. OS appears to be: MAC
        Build info: version: '2.41.0', revision: '3192d8a', time: '2014-03-27 17:17:32'
        System info: host: 'HiroshiOyamada-no-MacBook-Air.local', ip: '10.42.47.111', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.8.5', java.version: '1.6.0_65'
        Driver info: driver.version: FirefoxDriver

でも、最近Firefox使ってないし、Chrome使いたいのでNightwatch.jsでChromeのテストを参考にして、Chrome使う方法模索しました。

Chrome使うやり方に大分ハマった

ハマったポイントを先に書くと、ChromeのDriverをダウンロードして、それの保存先を間違っていたからでした。 どういうことかというとターミナル上で

which chromedriver

とした時に、chromedriverのロケーションが表示されるところにおけばすぐに動作したのですが、この辺りあまり深く考えず、~/Documents/chromedriverとかに置いてました

ChromeDriverのGetting startedを見てたら

Any of these steps should do the trick: include the ChromeDriver location in your PATH environment variable

という記述を見つけて、この時点で

「あ、PATHが通ってるところに置かないとダメっぽいのかな?」

というのに気づき、ひとまず/usr/sbin/にchromedriverをコピーしたら無事に実行できました

参考までにディレクトリ構成など

ディレクトリはこんな感じ

.
├── globals.json
├── reports
│   ├── craftbeerfan.xml
│   └── google.xml
├── screenshots
└── tests
    ├── craftbeerfan.js
    └── google.js

上記ディレクトリ内のglobals.jsonはこんな感じ

{
  "src_folders" : ["./tests"],
  "output_folder" : "./reports",
  "custom_commands_path" : "",

  "selenium" : {
    "start_process" : false,
    "server_path" : "/usr/local/Cellar/selenium-server-standalone/2.41.0/libexec/selenium-server-standalone-2.41.0.jar",
    "log_path" : "",
    "host" : "127.0.0.1",
    "port" : 4444
  },

  "test_settings" : {
    "default" : {
      "launch_url" : "http://localhost",
      "selenium_host" : "127.0.0.1",
      "selenium_port"  : 4444,
      "silent": true,
      "output": true,
      "firefox_profile": false,
      "chrome_driver" : "/usr/sbin/chromedriver",
      "screenshots" : {
        "enabled" : true,
        "path" : "./screenshots"
      },
      "desiredCapabilities": {
        "browserName": "chrome",
        "javascriptEnabled": true,
        "acceptSslCerts": true
      }
    }
  }
}

で実行する時には以下のようにやってます。

nightwatch -c ./globals.json -t tests/google.js --verbose

--verbose オプションは付けることで、

✔  Testing if element <#main> contains text: "The Night Watch".
LOG     → Completed command getText (1098 ms)
INFO Request: DELETE /wd/hub/session/7358e240-94be-49f9-a2af-c0ecc2fb29f8
 - data:
 - headers:  {"Content-Length":0}
INFO Response 204 DELETE /wd/hub/session/7358e240-94be-49f9-a2af-c0ecc2fb29f8 {}
LOG     → Completed command session (582 ms)
INFO FINISHED
OK. 5 assertions passed. (9568 ms)

という感じで詳細の情報がターミナル上に表示されるので一応このオプション付きでやってます

CraftBeerFanの管理ページ機能の自動テストも一応動作した

上記実行できたので、今度はCraftBeerFanのWebアプリについて

  • GoogleMapsが表示されることを確認する
  • 管理ページにログイン出来る

という簡単なテストもやってみました。

tests/以下にcraftbeerfan.jsというファイルを作って以下のようにしました

module.exports = {
  setup: function() {},
  teardown: function() {},
  "top page": function(client) {
    client.url("http://craftbeer-fan.herokuapp.com/")
      .waitForElementVisible("body", 3000)
      .assert.title("CraftBeerFan")
      .assert.visible("#map_canvas")
      .end();
  },
  "Login": function(client) {
    client.url("http://craftbeer-fan.herokuapp.com/login")
      .waitForElementVisible("body", 3000)
      .setValue('input[type=text]', 'USERID')
      .setValue('input[type=password]', 'PASSWORD')
      .click('.btn-danger')
      .pause(1000)
      .assert.title("管理者サイト")
      .end();
  }
};

上記実行すると、ブラウザが勝手に起動して無事にテストもパスしました

まとめ

Seleniumは、割と昔から憧れがあったのでそれが使えるのは個人的に魅力だし、あとはテストコードをNode.jsというかJavaScriptで書けるのは自分としては扱いやすいです

Nightwathc.jsのサイト見てると、カスタマイズしてコマンド拡張もできるようなので今後はこれを使ってくことにします。