JavaScriptでWebDriverのテストコードを書きましょ

37
JAVASCRIPT WEB DRIVER @kuronekomichael 福岡Haxe勉強会 feat. HTML5+α @福岡 - 第0x00回

description

ねこび〜ん by カネウチカズコ: http://ja.netbeans.org/nekobean drawn by Cacoo: http://cacoo.com/

Transcript of JavaScriptでWebDriverのテストコードを書きましょ

Page 1: JavaScriptでWebDriverのテストコードを書きましょ

JAVASCRIPT ♥ WEB DRIVER@kuronekomichael

福岡Haxe勉強会 feat. HTML5+α @福岡 - 第0x00回

Page 2: JavaScriptでWebDriverのテストコードを書きましょ

introduction

自動テストは誰もがやりたいと思うが、敷居が高い

特にUIテストは技術的にも運用的にも難しい

少しでも作業減らしたいよね

Web Driverは意外と簡単に使えるよ

JavaScriptで書けるのでディベロッパーも頑張れるよ

メンテナブルなテストコードを書こうよ

でもさ

Page 3: JavaScriptでWebDriverのテストコードを書きましょ

Web Driverとは

Googleが開発したWebアプリテストツール。

2011年にSeleniumと統合された。

Selenium2 === WebDriver

RESTfulなHTTPプロトコル「Json Wire Protocol」で

ブラウザの遠隔操作を実現

今日はRemote Web Driverの話だけやります

JsonWireProtocol: https://code.google.com/p/selenium/wiki/JsonWireProtocol

Page 4: JavaScriptでWebDriverのテストコードを書きましょ

WebアプリのUIテスト

プラットフォーム毎/ブラウザ毎に、同じことを何度も何度も・・・drawn by Cacoo: http://cacoo.com/

Page 5: JavaScriptでWebDriverのテストコードを書きましょ

あ、IE9でも確認しておかないと…

drawn by Cacoo: http://cacoo.com/

Page 6: JavaScriptでWebDriverのテストコードを書きましょ

Androidが追加!2.xと4.xは別物なの?!

drawn by Cacoo: http://cacoo.com/

Page 7: JavaScriptでWebDriverのテストコードを書きましょ

GalaxyS4だけおかしい?AQUOS Phoneも?何?部長がiOS7βにしただとおお?!?

drawn by Cacoo: http://cacoo.com/

Page 8: JavaScriptでWebDriverのテストコードを書きましょ

GAME OVER

部長マジくたばれ

Page 9: JavaScriptでWebDriverのテストコードを書きましょ

DEMO

Page 10: JavaScriptでWebDriverのテストコードを書きましょ

Web Driverの仕組み

・テストコードを元に操作を要求する「WebDriver クライアント」

・HTTP経由で要求を受け取ってブラウザを操作する「WebDriver サーバ」

 ブラウザを操作するための「ドライバ」(ブラウザ毎に用意されている)drawn by Cacoo: http://cacoo.com/ねこび~ん by カネウチカズコ: http://ja.netbeans.org/nekobean

Page 11: JavaScriptでWebDriverのテストコードを書きましょ

Web Driver Server

自前でWeb Driver Serverを準備したくないなら、Sause Labを使う手もあります https://saucelabs.com/php/se2/2

drawn by Cacoo: http://cacoo.com/ねこび~ん by カネウチカズコ: http://ja.netbeans.org/nekobean

Page 12: JavaScriptでWebDriverのテストコードを書きましょ

Web Driver ClientJson Wire Protocolに従ったhttpリクエスト/レスポンスが

処理できれば、実装言語は何でもいい

すでに言語毎に様々な実装有り(サードパーティ含む)

ねこび~ん by カネウチカズコ: http://ja.netbeans.org/nekobean

Page 13: JavaScriptでWebDriverのテストコードを書きましょ

JavaScriptでのテストコード実装Nodeで実行

ライブラリは選択肢多数

WebDriverJs(公式)

jwebdriver

webdriver.js

burnout

wd and etc.

今回は wd を使用

Page 14: JavaScriptでWebDriverのテストコードを書きましょ

wdでの実装例

var wd = require('wd'), assert = require('assert'), browser = wd.remote({hostname: '10.0.2.19', port: 8080});

browser.init({browserName:'android'}, function(err, sessionId) {

// ページを開く browser.get("http://demo.basercms.net/", function(err) {

// 要素を取得 browser.elementByCssSelector('#global_menu .menu04 a', function(err, el) {

// 要素の文字列をチェック el.text(function(err, text) { assert.equal(text, '新着情報');

// 終了 browser.quit(); }); }); });});

wdはサンプルも豊富なので参考に。Json Wire ProtocolとAPIの対比表は、読み方に慣れが必要かも…。

wd document: https://github.com/admc/wd

Page 15: JavaScriptでWebDriverのテストコードを書きましょ

より実践的に

Page 16: JavaScriptでWebDriverのテストコードを書きましょ

溢れだす欲求ページを開く前にセッション情報(Cookie)を入れたい

ページ毎にtitleが正しいかテストしたい

要素が存在するか判定したい

アンカーをクリックしたい

要素をタップしたい

エビデンス(スクリーンショット)を取りたい

非同期に表示される要素が出てから次に進みたい

などなどなどなど

Page 17: JavaScriptでWebDriverのテストコードを書きましょ

実践例

1)事前にセッション情報(Cookie)を入れる

2)ページを開いて、意図したタイトルかテスト

3)必須要素が存在するかテスト

4)特定の要素をクリックして意図したページへ遷移するかテスト

5)ページ毎にスクリーンショットを保存

Page 18: JavaScriptでWebDriverのテストコードを書きましょ

DEMO

Page 19: JavaScriptでWebDriverのテストコードを書きましょ

1)事前にセッション情報を入れる

// 古いセッション情報を削除browser.deleteAllCookies(function(err) { // セッション情報を設定 browser.setCookie({name:'uuid', value:'...'}, function(err) { // 続きの処理 });});

※いったん全てのCookieを削除しているのは、Android DriverでBrowserのCookieを引き継いでしまうのを防ぐため

Page 20: JavaScriptでWebDriverのテストコードを書きましょ

残念!他にもCookieを入れる必要がありました

Page 21: JavaScriptでWebDriverのテストコードを書きましょ

1)事前にセッション情報を入れる×3

// 古いセッション情報を削除browser.deleteAllCookies(function(err) {

// セッション情報を設定 browser.setCookie({name:'uuid', value:'...'}, function(err) {

browser.setCookie({name:'cookie-P', value:'...'}, function(err) { browser.setCookie({name:'tutorial_flag', value:'true'}, function(err) {

// 続きの処理 });

}); });

});

Page 22: JavaScriptでWebDriverのテストコードを書きましょ

さあ!胡散臭くなってまりいました!

Page 23: JavaScriptでWebDriverのテストコードを書きましょ

2) ページを開いてタイトルをテスト

// ページを開くbrowser.get("http://ncat.me/", function(err) {

assert.ifError(err);

// タイトルが意図した文字列かテスト browser.title(function(err, title) {

assert.ifError(err); assert.ok(~title.indexOf('ネガネガ ネガにゃんこ'));

// 続きの処理

});});

Page 24: JavaScriptでWebDriverのテストコードを書きましょ

3) 必須要素が存在するかテスト

// 画面が表示されるまで待つbrowser.waitForVisibleByCssSelector('#mypageBtnPortal', 10 * 1000, function(err) {

// 必須要素が存在するかテスト

browser.elementByCssSelector('#mypageBtnPortal', function(err, element) { assert.ifError(err);

// 続きの処理

});});

Page 25: JavaScriptでWebDriverのテストコードを書きましょ

4) 要素をクリックして遷移をテスト

// 特定の要素をクリックelement.click(function(err) {

assert.ifError(err);

// 遷移先ページが表示されるまで待つ browser.waitForVisibleByCssSelector('#btnBack', 10 * 1000, function(err) {

assert.ifError(err);

// 続きの処理 });

});

Page 26: JavaScriptでWebDriverのテストコードを書きましょ

5) スクリーンショットを保存

// スクリーンショットを撮る

browser.takeScreenshot(function(err, screenshot) { assert.ifError(err);

fs.writeFile('screenshot.png', screenshot, 'base64', function(err) { assert.ifError(err);

// もし続きがあればここに });

});

Page 27: JavaScriptでWebDriverのテストコードを書きましょ

browser.init({browserName: 'android'}, function(err, sessionId) { assert.ifError(err);

// ページを開く browser.get("http://ncat.me/dl/", function(err) { assert.ifError(err);

// 古いセッション情報を削除 browser.deleteAllCookies(function(err) { assert.ifError(err);

// セッション情報を設定 browser.setCookie({name:'uuid', value:'...'}, function(err) { assert.ifError(err); browser.setCookie({name:'cookie-P', value:'...'}, function(err) { assert.ifError(err); browser.setCookie({name:'tutorial_flag', value:'true'}, function(err) { assert.ifError(err);

// Cookie付きで再びページを開く browser.get("http://ncat.me/", function(err) { assert.ifError(err);

// タイトルが意図した文字列かテスト browser.title(function(err, title) { assert.ifError(err); assert.ok(~title.indexOf('ネガネガ ネガにゃんこ'));

// 画面が表示されるまで待つ browser.waitForVisibleByCssSelector('#mypageBtnPortal', 10 * 1000, function(err) {

// 必須要素が存在するかテスト browser.elementByCssSelector('#mypageBtnPortal', function(err, element) { assert.ifError(err);

// 特定の要素をクリックして意図したページに遷移するかテスト element.click(function(err) { assert.ifError(err); browser.waitForVisibleByCssSelector('#btnBack', 10 * 1000, function(err) { assert.ifError(err);

// スクリーンショットを撮る browser.takeScreenshot(function(err, screenshot) { assert.ifError(err); fs.writeFile('screenshot.png', screenshot, 'base64', function(err) { assert.ifError(err); browser.quit(); }); }); }); }); }); }); }); }); }); }); }); }); });});

Page 28: JavaScriptでWebDriverのテストコードを書きましょ

WELCOME TO CALLBACK HELL

here come a

Callback Monster

Copyright © 2013 Warner Bros. Pictures / Picture from http://www.zekefilm.org/2013/07/11/tag-team-review-pacific-rim/

Page 29: JavaScriptでWebDriverのテストコードを書きましょ

コールバック地獄コールバックの連鎖に陥る危険については、公式のドキュメントでも言及されているhttps://code.google.com/p/selenium/wiki/WebDriverJs#Understanding_the_API

対策

Control Flow?

Promise?

・そもそも関数がまたがるのは直感的じゃない・時系列に書きたい・読みたい

Page 30: JavaScriptでWebDriverのテストコードを書きましょ

REDEMPTION FROMCALLBACK HELL人類には

yieldがある・・・!Copyright © 2013 Warner Bros. Pictures / Picture from http://www.prairiedogmag.com/review-pacific-rim-delivers-quality-entertainment-but-little-else/

We don't give up

Page 31: JavaScriptでWebDriverのテストコードを書きましょ

REDEMPTION FROMCALLBACK HELL人類には

yieldがある・・・!Copyright © 2013 Warner Bros. Pictures / Picture from http://www.prairiedogmag.com/review-pacific-rim-delivers-quality-entertainment-but-little-else/

Page 32: JavaScriptでWebDriverのテストコードを書きましょ

yield/generator関数の実行を途中で中断して、必要に応じて再開する機能なんだ、夢でも見ているのか・・?

ECMA Script6で導入が決定している

1. 2006/10 FireFox2で独自実装(ECMA Script3拡張、JavaScipt1.7)

2. ECMAScript6(harmony)に導入決定次世代JavaScriptに入ることが確定

3. 先行してV8に実装完了

4. Chrome Canary(Chrome開発版)には既に導入済みNode 0.12以降に導入済み

ECMAScript 6draft: http://wiki.ecmascript.org/doku.php?id=harmony:generators

Page 33: JavaScriptでWebDriverのテストコードを書きましょ

yieldの簡単な使用例

function* asyncCode() {console.log('初めの処理');yield 1;console.log('何か終わった後の処理');return 2;

}

// generatorの生成(まだ関数は実行されない)var gen = asyncCode();

// 1回めの実行var ret = gen.next();

// コンソールには’初めの処理’が出力される// ret === {value:1, done:false}

// 2回めの実行ret = gen.next();

// コンソールには’何か終わった後の処理’が出力される// ret === {value:2, done:true}

Page 34: JavaScriptでWebDriverのテストコードを書きましょ

wd-syncwdを拡張したモジュール

yieldを使ってAPIを全て同期に置換えている

実はECMAScriptのyieldは使ってない(*ノω・*)テヘ

wd-syncは fibers を使って同期を実現している

fibersはJavaScriptだけではなくCのコードで同じ機能を実現させている

他にも無理矢理実現させているモジュールもあるみたい

(関数を文字列化してsetTimeoutで無理矢理分割とか...)

Page 35: JavaScriptでWebDriverのテストコードを書きましょ

wd-syncを使った実装sync(function() {

browser.init({browserName: 'android'});

// Cookieを設定するためにいったんサイトを開くbrowser.get("http://ncat.me/dl/");

// セッション情報を再設定browser.deleteAllCookies();browser.setCookie({name:'uuid', value:'...'});browser.setCookie({name:'cookie-P', value:'...'});browser.setCookie({name:'tutorial_flag', value:'true'});

// Cookieを設定したので、改めて開くbrowser.get("http://ncat.me/");

// タイトルが意図した文字列かテストvar title = browser.title();assert.ok(~title.indexOf('ネガネガ ネガにゃんこ'));

// 画面が表示されるまで待つbrowser.waitForVisibleByCssSelector('#mypageBtnPortal', 10 * 1000);

// 必須要素が存在するかテストvar element = browser.elementByCssSelector('#mypageBtnPortal');assert.ok(element);

// 特定の要素をクリックして意図したページに遷移するかテストelement.click();browser.waitForVisibleByCssSelector('#btnBack', 10 * 1000);

// スクリーンショットを取得var screenshot = browser.takeScreenshot();assert.ok(screenshot);fs.writeFileSync('screenshot.png', screenshot, 'base64');

});

Page 36: JavaScriptでWebDriverのテストコードを書きましょ

ending意外とWeb Driverは簡単

うまく動作しない時は、http req/resの中身を見る

Json Wire Protocolは理解しやすいので一読オススメ

コールバック地獄から抜けだそう

ブラウザのコードではまだ地獄が続くけど…

テストコードはシンプルが第一

やっぱりUIのテストは難しい

完璧は求めずに、やれることからやろう(not TDD)

スモークテストでいいじゃない

Page 37: JavaScriptでWebDriverのテストコードを書きましょ

御清聴あざした!