• Like
JavaScriptでWebDriverのテストコードを書きましょ
Upcoming SlideShare
Loading in...5
×

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

  • 4,219 views
Uploaded on

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

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

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
4,219
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
12
Comments
0
Likes
20

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. JAVASCRIPT ♥ WEB DRIVER @kuronekomichael 福岡Haxe勉強会 feat. HTML5+α @福岡 - 第0x00回
  • 2. introduction 自動テストは誰もがやりたいと思うが、敷居が高い 特にUIテストは技術的にも運用的にも難しい 少しでも作業減らしたいよね Web Driverは意外と簡単に使えるよ JavaScriptで書けるのでディベロッパーも頑張れるよ メンテナブルなテストコードを書こうよ でもさ
  • 3. 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
  • 4. WebアプリのUIテスト プラットフォーム毎/ブラウザ毎に、同じことを何度も何度も・・・ drawn by Cacoo: http://cacoo.com/
  • 5. あ、IE9でも確認しておかないと… drawn by Cacoo: http://cacoo.com/
  • 6. Androidが追加!2.xと4.xは別物なの?! drawn by Cacoo: http://cacoo.com/
  • 7. GalaxyS4だけおかしい?AQUOS Phoneも? 何?部長がiOS7βにしただとおお?!? drawn by Cacoo: http://cacoo.com/
  • 8. GAME OVER 部長マジくたばれ
  • 9. DEMO
  • 10. Web Driverの仕組み ・テストコードを元に操作を要求する「WebDriver クライアント」 ・HTTP経由で要求を受け取ってブラウザを操作する「WebDriver サーバ」  ブラウザを操作するための「ドライバ」(ブラウザ毎に用意されている) drawn by Cacoo: http://cacoo.com/ねこび∼ん by カネウチカズコ: http://ja.netbeans.org/nekobean
  • 11. 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
  • 12. Web Driver Client Json Wire Protocolに従ったhttpリクエスト/レスポンスが 処理できれば、実装言語は何でもいい すでに言語毎に様々な実装有り(サードパーティ含む) ねこび∼ん by カネウチカズコ: http://ja.netbeans.org/nekobean
  • 13. JavaScriptでのテストコード実装 Nodeで実行 ライブラリは選択肢多数 WebDriverJs(公式) jwebdriver webdriver.js burnout wd and etc. 今回は wd を使用
  • 14. 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
  • 15. より実践的に
  • 16. 溢れだす欲求 ページを開く前にセッション情報(Cookie)を入れたい ページ毎にtitleが正しいかテストしたい 要素が存在するか判定したい アンカーをクリックしたい 要素をタップしたい エビデンス(スクリーンショット)を取りたい 非同期に表示される要素が出てから次に進みたい などなどなどなど
  • 17. 実践例 1)事前にセッション情報(Cookie)を入れる 2)ページを開いて、意図したタイトルかテスト 3)必須要素が存在するかテスト 4)特定の要素をクリックして 意図したページへ遷移するかテスト 5)ページ毎にスクリーンショットを保存
  • 18. DEMO
  • 19. 1)事前にセッション情報を入れる // 古いセッション情報を削除 browser.deleteAllCookies(function(err) { // セッション情報を設定 browser.setCookie({name:'uuid', value:'...'}, function(err) { // 続きの処理 }); }); ※いったん全てのCookieを削除しているのは、Android DriverでBrowserのCookieを引き継いでしまうのを防ぐため
  • 20. 残念! 他にもCookieを入れる必要がありました
  • 21. 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) { // 続きの処理 }); }); }); });
  • 22. さあ!胡散臭くなってまりいました!
  • 23. 2) ページを開いてタイトルをテスト // ページを開く browser.get("http://ncat.me/", function(err) { assert.ifError(err); // タイトルが意図した文字列かテスト browser.title(function(err, title) { assert.ifError(err); assert.ok(~title.indexOf('ネガネガ ネガにゃんこ')); // 続きの処理 }); });
  • 24. 3) 必須要素が存在するかテスト // 画面が表示されるまで待つ browser.waitForVisibleByCssSelector('#mypageBtnPortal', 10 * 1000, function(err) { // 必須要素が存在するかテスト browser.elementByCssSelector('#mypageBtnPortal', function(err, element) { assert.ifError(err); // 続きの処理 }); });
  • 25. 4) 要素をクリックして遷移をテスト // 特定の要素をクリック element.click(function(err) { assert.ifError(err); // 遷移先ページが表示されるまで待つ browser.waitForVisibleByCssSelector('#btnBack', 10 * 1000, function(err) { assert.ifError(err); // 続きの処理 }); });
  • 26. 5) スクリーンショットを保存 // スクリーンショットを撮る browser.takeScreenshot(function(err, screenshot) { assert.ifError(err); fs.writeFile('screenshot.png', screenshot, 'base64', function(err) { assert.ifError(err); // もし続きがあればここに }); });
  • 27. 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(); }); }); }); }); }); }); }); }); }); }); }); }); }); });
  • 28. 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/
  • 29. コールバック地獄 コールバックの連鎖に陥る危険については、 公式のドキュメントでも言及されている https://code.google.com/p/selenium/wiki/WebDriverJs#Understanding_the_API 対策 Control Flow? Promise? ・そもそも関数がまたがるのは直感的じゃない ・時系列に書きたい・読みたい
  • 30. REDEMPTION FROM CALLBACK 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
  • 31. REDEMPTION FROM CALLBACK HELL 人類には yieldがある・・・! Copyright © 2013 Warner Bros. Pictures / Picture from http://www.prairiedogmag.com/review-pacific-rim-delivers-quality-entertainment-but-little-else/
  • 32. 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
  • 33. 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}
  • 34. wd-sync wdを拡張したモジュール yieldを使ってAPIを全て同期に置換えている 実はECMAScriptのyieldは使ってない(*ノω・*)テヘ wd-syncは fibers を使って同期を実現している fibersはJavaScriptだけではなくCのコードで同じ機能を実現させている 他にも無理矢理実現させているモジュールもあるみたい (関数を文字列化してsetTimeoutで無理矢理分割とか...)
  • 35. 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'); });
  • 36. ending 意外とWeb Driverは簡単 うまく動作しない時は、http req/resの中身を見る Json Wire Protocolは理解しやすいので一読オススメ コールバック地獄から抜けだそう ブラウザのコードではまだ地獄が続くけど… テストコードはシンプルが第一 やっぱりUIのテストは難しい 完璧は求めずに、やれることからやろう(not TDD) スモークテストでいいじゃない
  • 37. 御清聴あざした!