Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

of

JS開発におけるTDDと自動テストツール利用の勘所 Slide 1 JS開発におけるTDDと自動テストツール利用の勘所 Slide 2 JS開発におけるTDDと自動テストツール利用の勘所 Slide 3 JS開発におけるTDDと自動テストツール利用の勘所 Slide 4 JS開発におけるTDDと自動テストツール利用の勘所 Slide 5 JS開発におけるTDDと自動テストツール利用の勘所 Slide 6 JS開発におけるTDDと自動テストツール利用の勘所 Slide 7 JS開発におけるTDDと自動テストツール利用の勘所 Slide 8 JS開発におけるTDDと自動テストツール利用の勘所 Slide 9 JS開発におけるTDDと自動テストツール利用の勘所 Slide 10 JS開発におけるTDDと自動テストツール利用の勘所 Slide 11 JS開発におけるTDDと自動テストツール利用の勘所 Slide 12 JS開発におけるTDDと自動テストツール利用の勘所 Slide 13 JS開発におけるTDDと自動テストツール利用の勘所 Slide 14 JS開発におけるTDDと自動テストツール利用の勘所 Slide 15 JS開発におけるTDDと自動テストツール利用の勘所 Slide 16 JS開発におけるTDDと自動テストツール利用の勘所 Slide 17 JS開発におけるTDDと自動テストツール利用の勘所 Slide 18 JS開発におけるTDDと自動テストツール利用の勘所 Slide 19 JS開発におけるTDDと自動テストツール利用の勘所 Slide 20 JS開発におけるTDDと自動テストツール利用の勘所 Slide 21 JS開発におけるTDDと自動テストツール利用の勘所 Slide 22 JS開発におけるTDDと自動テストツール利用の勘所 Slide 23 JS開発におけるTDDと自動テストツール利用の勘所 Slide 24 JS開発におけるTDDと自動テストツール利用の勘所 Slide 25 JS開発におけるTDDと自動テストツール利用の勘所 Slide 26 JS開発におけるTDDと自動テストツール利用の勘所 Slide 27 JS開発におけるTDDと自動テストツール利用の勘所 Slide 28 JS開発におけるTDDと自動テストツール利用の勘所 Slide 29 JS開発におけるTDDと自動テストツール利用の勘所 Slide 30 JS開発におけるTDDと自動テストツール利用の勘所 Slide 31 JS開発におけるTDDと自動テストツール利用の勘所 Slide 32 JS開発におけるTDDと自動テストツール利用の勘所 Slide 33 JS開発におけるTDDと自動テストツール利用の勘所 Slide 34 JS開発におけるTDDと自動テストツール利用の勘所 Slide 35 JS開発におけるTDDと自動テストツール利用の勘所 Slide 36 JS開発におけるTDDと自動テストツール利用の勘所 Slide 37 JS開発におけるTDDと自動テストツール利用の勘所 Slide 38 JS開発におけるTDDと自動テストツール利用の勘所 Slide 39 JS開発におけるTDDと自動テストツール利用の勘所 Slide 40 JS開発におけるTDDと自動テストツール利用の勘所 Slide 41 JS開発におけるTDDと自動テストツール利用の勘所 Slide 42 JS開発におけるTDDと自動テストツール利用の勘所 Slide 43 JS開発におけるTDDと自動テストツール利用の勘所 Slide 44 JS開発におけるTDDと自動テストツール利用の勘所 Slide 45 JS開発におけるTDDと自動テストツール利用の勘所 Slide 46 JS開発におけるTDDと自動テストツール利用の勘所 Slide 47 JS開発におけるTDDと自動テストツール利用の勘所 Slide 48 JS開発におけるTDDと自動テストツール利用の勘所 Slide 49 JS開発におけるTDDと自動テストツール利用の勘所 Slide 50 JS開発におけるTDDと自動テストツール利用の勘所 Slide 51 JS開発におけるTDDと自動テストツール利用の勘所 Slide 52 JS開発におけるTDDと自動テストツール利用の勘所 Slide 53 JS開発におけるTDDと自動テストツール利用の勘所 Slide 54 JS開発におけるTDDと自動テストツール利用の勘所 Slide 55 JS開発におけるTDDと自動テストツール利用の勘所 Slide 56 JS開発におけるTDDと自動テストツール利用の勘所 Slide 57 JS開発におけるTDDと自動テストツール利用の勘所 Slide 58 JS開発におけるTDDと自動テストツール利用の勘所 Slide 59 JS開発におけるTDDと自動テストツール利用の勘所 Slide 60 JS開発におけるTDDと自動テストツール利用の勘所 Slide 61 JS開発におけるTDDと自動テストツール利用の勘所 Slide 62 JS開発におけるTDDと自動テストツール利用の勘所 Slide 63 JS開発におけるTDDと自動テストツール利用の勘所 Slide 64 JS開発におけるTDDと自動テストツール利用の勘所 Slide 65 JS開発におけるTDDと自動テストツール利用の勘所 Slide 66 JS開発におけるTDDと自動テストツール利用の勘所 Slide 67 JS開発におけるTDDと自動テストツール利用の勘所 Slide 68 JS開発におけるTDDと自動テストツール利用の勘所 Slide 69 JS開発におけるTDDと自動テストツール利用の勘所 Slide 70 JS開発におけるTDDと自動テストツール利用の勘所 Slide 71 JS開発におけるTDDと自動テストツール利用の勘所 Slide 72 JS開発におけるTDDと自動テストツール利用の勘所 Slide 73 JS開発におけるTDDと自動テストツール利用の勘所 Slide 74 JS開発におけるTDDと自動テストツール利用の勘所 Slide 75 JS開発におけるTDDと自動テストツール利用の勘所 Slide 76 JS開発におけるTDDと自動テストツール利用の勘所 Slide 77 JS開発におけるTDDと自動テストツール利用の勘所 Slide 78 JS開発におけるTDDと自動テストツール利用の勘所 Slide 79 JS開発におけるTDDと自動テストツール利用の勘所 Slide 80 JS開発におけるTDDと自動テストツール利用の勘所 Slide 81 JS開発におけるTDDと自動テストツール利用の勘所 Slide 82 JS開発におけるTDDと自動テストツール利用の勘所 Slide 83 JS開発におけるTDDと自動テストツール利用の勘所 Slide 84 JS開発におけるTDDと自動テストツール利用の勘所 Slide 85 JS開発におけるTDDと自動テストツール利用の勘所 Slide 86 JS開発におけるTDDと自動テストツール利用の勘所 Slide 87 JS開発におけるTDDと自動テストツール利用の勘所 Slide 88 JS開発におけるTDDと自動テストツール利用の勘所 Slide 89 JS開発におけるTDDと自動テストツール利用の勘所 Slide 90 JS開発におけるTDDと自動テストツール利用の勘所 Slide 91 JS開発におけるTDDと自動テストツール利用の勘所 Slide 92 JS開発におけるTDDと自動テストツール利用の勘所 Slide 93 JS開発におけるTDDと自動テストツール利用の勘所 Slide 94 JS開発におけるTDDと自動テストツール利用の勘所 Slide 95 JS開発におけるTDDと自動テストツール利用の勘所 Slide 96 JS開発におけるTDDと自動テストツール利用の勘所 Slide 97 JS開発におけるTDDと自動テストツール利用の勘所 Slide 98 JS開発におけるTDDと自動テストツール利用の勘所 Slide 99 JS開発におけるTDDと自動テストツール利用の勘所 Slide 100 JS開発におけるTDDと自動テストツール利用の勘所 Slide 101 JS開発におけるTDDと自動テストツール利用の勘所 Slide 102 JS開発におけるTDDと自動テストツール利用の勘所 Slide 103 JS開発におけるTDDと自動テストツール利用の勘所 Slide 104 JS開発におけるTDDと自動テストツール利用の勘所 Slide 105 JS開発におけるTDDと自動テストツール利用の勘所 Slide 106 JS開発におけるTDDと自動テストツール利用の勘所 Slide 107 JS開発におけるTDDと自動テストツール利用の勘所 Slide 108 JS開発におけるTDDと自動テストツール利用の勘所 Slide 109 JS開発におけるTDDと自動テストツール利用の勘所 Slide 110 JS開発におけるTDDと自動テストツール利用の勘所 Slide 111 JS開発におけるTDDと自動テストツール利用の勘所 Slide 112 JS開発におけるTDDと自動テストツール利用の勘所 Slide 113 JS開発におけるTDDと自動テストツール利用の勘所 Slide 114 JS開発におけるTDDと自動テストツール利用の勘所 Slide 115 JS開発におけるTDDと自動テストツール利用の勘所 Slide 116 JS開発におけるTDDと自動テストツール利用の勘所 Slide 117 JS開発におけるTDDと自動テストツール利用の勘所 Slide 118 JS開発におけるTDDと自動テストツール利用の勘所 Slide 119 JS開発におけるTDDと自動テストツール利用の勘所 Slide 120 JS開発におけるTDDと自動テストツール利用の勘所 Slide 121 JS開発におけるTDDと自動テストツール利用の勘所 Slide 122 JS開発におけるTDDと自動テストツール利用の勘所 Slide 123 JS開発におけるTDDと自動テストツール利用の勘所 Slide 124 JS開発におけるTDDと自動テストツール利用の勘所 Slide 125 JS開発におけるTDDと自動テストツール利用の勘所 Slide 126 JS開発におけるTDDと自動テストツール利用の勘所 Slide 127 JS開発におけるTDDと自動テストツール利用の勘所 Slide 128 JS開発におけるTDDと自動テストツール利用の勘所 Slide 129 JS開発におけるTDDと自動テストツール利用の勘所 Slide 130
Upcoming SlideShare
MochaとChaiでやるJavaScriptテスト
Next

172 Likes

Share

JS開発におけるTDDと自動テストツール利用の勘所

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

JS開発におけるTDDと自動テストツール利用の勘所

  1. 1. JS開発における TDDと自動テスト ツール利用の勘所 2012.12.06 株式会社マピオン 中村 浩士 12年12月5日水曜日
  2. 2. 自己紹介 中村 浩士 ( @kozy4324 ) 株式会社マピオン所属 主にWebアプリのフロントエンド開発 JavaScript, ActionScript 12年12月5日水曜日
  3. 3. Mapion 12年12月5日水曜日
  4. 4. Mapion 地図情報検索サイト 月間7600万PV、1200万UU 全文検索エンジンSolrを利用した900万件超 のスポット情報を検索できる電話帳/地図面 その他位置情報コンテンツやナビサービス 2008年からスマートフォンサイトにも注力 Android、iOSのネイティブアプリ開発も 12年12月5日水曜日
  5. 5. 地図を使ったWebアプリ よく開発しています 12年12月5日水曜日
  6. 6. 今回 話すること TDDの基本となるJSユニットテストツールの 使い方 WebアプリでのTDDを意識した設計について (少しだけ) 様々なツールを利用してTDD/自動テストの効 率化を試みる話 12年12月5日水曜日
  7. 7. 話しないこと TDD自体について 詳細なやり方、あるべき論 WebアプリでのTDDベストプラクティス 僕はまだその答えに辿り着いていないです... 「テスト駆動JavaScript」が良書なので、それを読みましょう Webアプリに対するシナリオベースの     自動テストについて ユーザー操作をエミュレートしてWebアプ リ全体の振る舞いを自動でテストする方法 12年12月5日水曜日
  8. 8. アジェンダ ブラウザ上で実行するJSユニットテストツール 各ツール比較 使い方&コードサンプル WebアプリのTDDを意識した設計について TDDや自動テストで活用できる各種ツール コマンドライン環境 ヘッドレスブラウザ 自動テストツール CI環境 12年12月5日水曜日
  9. 9. ブラウザ上で実行する JSユニットテストツール 12年12月5日水曜日
  10. 10. TDDとは Test-Driven Development(テスト駆動開発) 分析技法、設計技法( テスト技法) 正しく動くソフトウェアを確実に作り上げるため のテクニック 進め方 1. テストを書く(テストファースト) 2. テストをパスする最低限の実装を行う 3. テストのパスを保持したままコードの重複を除 去する(リファクタリング) 4. 1∼3を短いスパンで繰り返す 12年12月5日水曜日
  11. 11. TDDの効果 書いたプログラムに対する即座のフィードバック 要求の理解の促進 リファクタリングの支援、クリーンコードの促進 自動テストによるデグレード検知 プログラマが持つ不安の解消 心の健康をもたらす :) 12年12月5日水曜日
  12. 12. JSユニットテストツール JsUnit YUI Test Google Closure Tools QUnit Jasmine Mocha Vows (etc...) 12年12月5日水曜日
  13. 13. JSユニットテストツール JsUnit YUI Test Google Closure Tools QUnit 自分がよく利用するのはこの4つ QUnit, Jasmine, Mochaはブラウザ上で実行可能 Jasmine Mocha Vows (etc...) 12年12月5日水曜日
  14. 14. ざっくり比較 非同期 スタイル ブラウザ実行 CLI実行 サポート シンプル QUnit フラット ○ △ ○ ブラウザ実行に最適 Rubyist向け Jasmine BDD ○ ○ ○ Jasmine-gemくそ便利 BDD, TDD, Exports, Nodeモジュール Mocha フラットが選べる ○ ○ ○ フレキシブル Nodeモジュール Vows Exports ○ ○ Nodeの非同期処理テストが スマートに書ける 12年12月5日水曜日
  15. 15. どれを利用すればよい? 12年12月5日水曜日
  16. 16. ケース別 プロジェクトへの導入が目的 シンプルなQUnitがオススメ Ruby / Ruby on Railsがメインの領域な人 Jasmineがオススメ CLI得意 / Node.jsもやりたい! Mocha, Vowsがいいのでは? 12年12月5日水曜日
  17. 17. QUnitとJasmineの 使い方を紹介 12年12月5日水曜日
  18. 18. QUnit 12年12月5日水曜日
  19. 19. QUnitとは? ブラウザ上での実行を想定したJSユニットテ ストフレームワーク jQueryの開発に利用されている シンプルさが特徴 MITライセンス 現在のリリースバージョンは v1.10.0 12年12月5日水曜日
  20. 20. http://qunitjs.com サイトからjsとcssをダウンロードして利用可能 12年12月5日水曜日
  21. 21. npmインストールの場合 パッケージ指定してインストール $ npm install qunitjs $ ls node_modules/qunitjs/qunit/ qunit.css!qunit.js もしくはpackage.json記述してインストール $ cat package.json {   "name": "sample-of-tdd",   "version": "1.0.0",   "devDependencies": {     "qunitjs": "1.10.0"   } } $ npm install 12年12月5日水曜日
  22. 22. HTML記述例 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>objects</title> qunit.js, qunit.css, テスト対象のjsファイル, テスト <link rel="stylesheet" href="qunit.css"> コードを記述したjsファイルの4リソースを読み込む <script src="qunit.js"></script> titleを設定することを強く推奨 <script src="objects.js"></script> <script src="objects_test.js"></script> </head> <body> <div id="qunit"></div> id="qunit"の要素に結果が出力される <div id="qunit-fixture"></div> id="qunit-fixture"はテスト実行の度に初期状態に復元 </doby> されるのでDOMに依存したテストの場合に利用できる </html> 12年12月5日水曜日
  23. 23. テストの基本構造 module("Object"); test("#methodA", function(assert) { module → test → アサーションの3階層   assert.ok(true, "some messages"); }); test("#methodB", function(assert) {   assert.ok(true, "some messages");   assert.ok(true, "some messages"); }); module("Array"); 次のmodule関数を呼ぶまでのtest関数がグルーピングされる test("#methodA", function(assert) {   assert.ok(true, "some messages"); (関数をネストする形ではない → フラットな形式)   assert.ok(true, "some messages"); }); test("#methodB", function(assert) {   assert.ok(true, "some messages");   assert.ok(true, "some messages");   assert.ok(true, "some messages"); }); 12年12月5日水曜日
  24. 24. アサーション ok(state[, message]) equal(actual, expected[, message]) notEqual(actual, expected[, message]) deepEqual(actual, expected[, message]) notDeepEqual(actual, expected[, message]) strictEqual(actual, expected[, message]) notStrictEqual(actual, expected[, message]) throws(block, expected[, message]) CommonJS Unit Testingの仕様に追従している 12年12月5日水曜日
  25. 25. setup/teardown module("Object", {   setup: function() {     this.object = new MyObject(); module()の第2引数のオブジェクトにsetupとteardownを設定   },   teardown: function() { で、テスト実行毎の前処理/後処理が行える     // do something...   } }); test("#methodA", function(assert) {   assert.ok(this.object.methodA()); thisでスコープが共有(ただしテスト毎のthisは別オブジェクト) }); test("#methodB", function(assert) {   assert.ok(this.object.methodB()); }); module("Array", {   setup: function() {     this.array = new MyArray(); setup/teardownはmodule単位で別に設定できる   } }); test("#methodA", function(assert) {   assert.ok(this.array.methodA()); }); test("#methodB", function(assert) {   assert.ok(this.array.methodB()); }); 12年12月5日水曜日
  26. 26. expect() test("#forEach with 1 item", 1, function(assert) {   [1].forEach(function(){ テスト内のアサーション数をチェック     assert.ok(true);   }); コールバック振る舞いの確認に利用可能 }); test()の引数に指定もしくは test("#forEach with 2 items", function(assert) { expect()関数で指定する   expect(2);   [1,2].forEach(function(){     assert.ok(true);   }); }); ただし、expect()だけでのコールバック振る舞いテストは貧弱なので 複雑なケースはSinon.jsを利用したほうがよい 12年12月5日水曜日
  27. 27. 非同期処理のテスト test("asyncTest A", function(assert) {   expect(1); stop()で次テストの実行を保留   setTimeout(function() { start()で保留を解除する     assert.ok(true);     start();   }, 1000);   stop(); }); asyncTest("asyncTest B", function(assert) { test() → asyncTest()とすることで   expect(1); stop()を省略できる   setTimeout(function() {     assert.ok(true);     start();   }, 1000); }); 12年12月5日水曜日
  28. 28. 実行結果 12年12月5日水曜日
  29. 29. 実行結果 onでグローバルへの変数汚染を チェックするモードで再実行 モジュールでの絞り込み実行も可能 リストをクリックすると詳細を開閉 (エラー時は最初から開いている) Rerun選択 or ダブルクリックで 特定テストのみ再実行 再実行時はfailしたテストから実行する(sessionStorage利用してる) その仕様を知らずに順番に依存したテストを書くと死ねます... 12年12月5日水曜日
  30. 30. failした時はこんな感じ 12年12月5日水曜日
  31. 31. Jasmine 12年12月5日水曜日
  32. 32. Jasmineとは? RubyのRSpecライクな記法のBDD(ビヘイ ビア駆動開発)フレームワーク 豊富なExpectationsとMatchers (QUnitで言うアサーション) spyによるTest Double(テスト代役) プラガブルなReporter MITライセンス 現在のリリースバージョンは v1.3.0 12年12月5日水曜日
  33. 33. https://github.com/pivotal/jasmine GitHubプロジェクトページのDownloadsにある zipファイルをダウンロードして解凍 12年12月5日水曜日
  34. 34. 補足:関連プロダクト GitHub: pivotal/jasmine-gem RubyGems: jasmine npm: - 依存 RackやSeleniumを含めた実行ヘルパー GitHub: pivotal/jasmine RubyGems: jasmine-core npm: - JavaScriptのフレームワーク部分 GitHub: mhevery/jasmine-node ダウンロードした 依存 RubyGems: - standalone版はコレ npm: jasmine-node Nodeで実行するためのCLIラッパー jasmine-gemやjasmine-nodeについては後半で 12年12月5日水曜日
  35. 35. zipファイルの中身 $ tree . !"" SpecRunner.html htmlがすでにサンプルとして !"" lib 動くものになっている #   $"" jasmine-1.3.0 #   !"" MIT.LICENSE #   !"" jasmine-html.js #   !"" jasmine.css #   $"" jasmine.js !"" spec #   !"" PlayerSpec.js #   $"" SpecHelper.js $"" src     !"" Player.js     $"" Song.js 4 directories, 9 files 12年12月5日水曜日
  36. 36. SpecRunner.htmlの中身 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head>   <title>Jasmine Spec Runner</title>   <link rel="shortcut icon" type="image/png" href="lib/jasmine-1.3.0/jasmine_favicon.png">   <link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.0/jasmine.css">   <script type="text/javascript" src="lib/jasmine-1.3.0/jasmine.js"></script>   <script type="text/javascript" src="lib/jasmine-1.3.0/jasmine-html.js"></script>   <!-- include source files here... -->   <script type="text/javascript" src="src/Player.js"></script>   <script type="text/javascript" src="src/Song.js"></script>   <!-- include spec files here... -->   <script type="text/javascript" src="spec/SpecHelper.js"></script>   <script type="text/javascript" src="spec/PlayerSpec.js"></script>   <script type="text/javascript">     (function() {       var jasmineEnv = jasmine.getEnv();       jasmineEnv.updateInterval = 1000; テスト対象のコードとスペックファイル(テストコード)を : それぞれ追加していけばよい (がっつり初期化処理が書いてあるので省略) :     })();   </script> </head> <body> </body> </html> 12年12月5日水曜日
  37. 37. テストの基本構造 describe("Array", function() {   describe(".isArray", function() {     it("should return true when called with an array", function() {       expect(Array.isArray([])).toBeTruthy();     });   });   describe("(has no item)", function() {     describe("#join", function() {       it("should return an empty string", function() {         expect([].join()).toEqual("");       });     });   }); }); describe → it → expectationsの3階層 describeはネストして記述することが可能 ex) describe → describe → it → expectations 12年12月5日水曜日
  38. 38. Matchers expect(x).toEqual(y) expect(x).not.toEqual(y) expect(x).toBe(y) notで否定のMatcherとなる expect(x).toMatch(pattern) expect(x).toBeDefined() toBeは === による等値チェック expect(x).toBeUndefined() expect(x).toBeNull() expect(x).toBeNaN() expect(x).toBeTruthy() expect(x).toBeFalsy() expect(x).toContain(y) expect(x).toBeLessThan(y) expect(x).toBeGreaterThan(y) expect(x).toBeCloseTo(y, precision) expect(function(){fn();}).toThrow(e) expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalledWith(arguments) and more ... 詳しくはGitHubのwikiページを参照 12年12月5日水曜日
  39. 39. beforeEach/afterEach describe("Object", function() { var object;   beforeEach(function() {     object = new MyObject(); describeのスコープ内でbeforeEach/afterEachを設定すること   });   afterEach(function() { で、 テスト実行毎の前処理/後処理が行える     // do something...   }); describe("#methodA", function() {    it("should be ok", function() { expect(object.methodA()).toBeTruthy(); }); }); describe("#methodB", function() {    it("should be ok", function() { expect(object.methodB()).toBeTruthy(); }); }); describe("(context)", function() { beforeEach(function() { object.someMethod(); }); describe("#methodC", function() {    it("should be ok", function() { expect(object.methodC()).toBeTruthy(); }); }); }); ネストしたdescribeそれぞれで設定した場合、親子関係の順でコールバックされる }); 親のbeforeEach → 子のbeforeEach → 子のafterEach → 親のafterEach 12年12月5日水曜日
  40. 40. spy it("should be called", function() { spyOnメソッドでオブジェクトの var obj = {method: function() {}}; 特定メソッドをスパイ化 spyOn(obj, "method"); obj.method(); spy用のMatcherが用意されている expect(obj.method).toHaveBeenCalled(); }); 詳しくはGitHubのwikiページを参照 test("should be called", function() { jasmine.createSpy()関数でスパイ化 var spy = jasmine.createSpy(); spy(); された関数オブジェクトを作成 expect(spy).toHaveBeenCalled(); }); Jasmineのspyオブジェクトは強力で十分な機能を 有しているが、Sinon.jsのほうが高機能 12年12月5日水曜日
  41. 41. 非同期処理のテスト it("should be async", function() {   runs(function() { 非同期処理ブロックはruns()で定義される     expect(true).toBeTruthy();   }); waits()で次のブロックの実行を、指定した   waits(500); ミリ秒間保留する   var spy = jasmine.createSpy();   runs(function() {     setTimeout(spy, 1000);   });   waitsFor(function() { waitsFor()はコールバックがtrueを返す     return spy.callCount > 0;   }); まで、次のブロック実行を保留する   runs(function() {     expect(true).toBeTruthy();   }); }); 12年12月5日水曜日
  42. 42. 実行結果 12年12月5日水曜日
  43. 43. 実行結果 spec毎のpass or failの結果 specのテキストをクリックすると、 該当スペックのみを再実行 12年12月5日水曜日
  44. 44. failした時はこんな感じ 12年12月5日水曜日
  45. 45. QUnit vs Jasmine 12年12月5日水曜日
  46. 46. module("Array"); test(".isArray", function(assert) {   assert.ok(Array.isArray([]), "Arrayでtrue"); }); module("Array.prototype", {   setup: function() {     this.array = [1,2,3];     this.empty_array = [];   } }); test("#concat", function(assert) {   assert.deepEqual(this.array.concat(), [1,2,3], "引数なしは配列のコピーを返す");   assert.deepEqual(this.array.concat(4), [1,2,3,4], "引数を末尾に連結した配列を返す");   assert.deepEqual(this.array.concat(4,5), [1,2,3,4,5], "引数は可変長に指定できる");   assert.deepEqual(this.array.concat([4,5]), [1,2,3,4,5], "配列は展開されて連結される"); }); test("#join", function(assert) {   assert.equal(this.array.join(), "1,2,3", "カンマで連結された文字列を返す");   assert.equal(this.array.join("-"), "1-2-3", "引数の文字列で連結された文字列を返す");   assert.equal(this.empty_array.join(), "", "要素がない配列は空文字列を返す");   assert.equal(this.empty_array.join("-"), "", "セパレーターを指定しても空文字列"); }); test("#pop", function(assert) {   assert.equal(this.array.pop(), 3, "末尾の要素を返す");   assert.deepEqual(this.array, [1,2], "戻り値の要素が削除される");   assert.equal(this.empty_array.pop(), undefined, "空配列はundefinedを返す"); }); test("#push", function(assert) {   assert.equal(this.empty_array.push(1), 4, "引数の要素を追加した後のサイズを返す");   assert.deepEqual(this.empty_array, [1,2,3,1], "要素が配列に追加される");   assert.equal(this.empty_array.push(2,3), 6, "引数の要素を追加した後のサイズを返す");   assert.deepEqual(this.empty_array, [1,2,3,1,2,3], "全ての要素が配列に追加される"); }); 12年12月5日水曜日
  47. 47. describe("Array", function() {   describe(".isArray", function() {     it("should return true when called with an array", function() {       expect(Array.isArray([])).toBeTruthy();     });   });   describe("(has 3 items)", function() {     var array;     beforeEach(function() {       array = [1,2,3];     });     describe("#concat", function() {       it("should return an array of own copy when called with no argument", function() {         expect(array.concat()).toEqual([1,2,3]);       });       it("should return an array including passed argument", function() {         expect(array.concat(4)).toEqual([1,2,3,4]);         expect(array.concat(4,5)).toEqual([1,2,3,4,5]);       });       it("should return an array including passed argument with array splatting", function() {         expect(array.concat([4,5])).toEqual([1,2,3,4,5]);       });     });     describe("#join", function() {       it("should return a string joined items with comma when called with no argument", function() {         expect(array.join()).toBe("1,2,3");       });       it("should return a string joined items with passed argument", function() {         expect(array.join("-")).toBe("1-2-3");       });     });     describe("#pop", function() {       it("should return and remove the last item", function() {         expect(array.pop()).toBe(3);         expect(array).toEqual([1,2]);       });     });     describe("#push", function() {       it("should add arguments into own, and return own size", function() {         expect(array.push(1)).toBe(4);         expect(array).toEqual([1,2,3,1]);         expect(array.push(2,3)).toBe(6);         expect(array).toEqual([1,2,3,1,2,3]);       });     });   });   describe("(has no item)", function() {     var array;     beforeEach(function() {       array = [];     });     describe("#join", function() {       it("should return an empty string", function() {         expect(array.join()).toBe("");         expect(array.join("-")).toBe("");       });     });     describe("#pop", function() {       it("should return undefined", function() {         expect(array.pop()).toBeUndefined();       });     });   }); }); 12年12月5日水曜日
  48. 48. QUnit vs Jasmine ブラウザ上の実行では基本機能は同等 記述スタイルの違い、好みの問題 QUnitはボキャブラリーが絞られるので  簡潔にならざるを得ない、表現力は劣る Jasmineは構造化しやすいがネストが深く なりがち(平均3∼5)、記述量も多め 12年12月5日水曜日
  49. 49. WebアプリのTDDを 意識した設計について 12年12月5日水曜日
  50. 50. TDDやりづらい実装 host objectに強依存 host objectとは実行環境から提供されるオ ブジェクト ex) window, navigator, location, etc... DOMオブジェクトに強依存 12年12月5日水曜日
  51. 51. host objectに強依存 location.searchのクエリーストリングをオブ ジェクトに変換する関数の実装 function parseQuery() {   var obj = {}, kvs = location.search.substring(1).split("&");   kvs.forEach(function(kv){obj[kv.split("=")[0]]=kv.split("=")[1]});   return obj; } query = parseQuery(); locationオブジェクトへの参照を外に出すだ けでユニットテストは書きやすくなる function parseQuery(search) {   var obj = {}, kvs = search.substring(1).split("&");   kvs.forEach(function(kv){obj[kv.split("=")[0]]=kv.split("=")[1]});   return obj; } query = parseQuery(location.search); 12年12月5日水曜日
  52. 52. host objectに強依存 どうしても引数を指定しないI/Fを作成したい のであれば、ラッパー関数で分離 function parseQuery() {   return _parseQuery(location.search); } function _parseQuery(search) {   var obj = {}, kvs = search.substring(1).split("&");   kvs.forEach(function(kv){obj[kv.split("=")[0]]=kv.split("=")[1]});   return obj; } query = parseQuery(); 12年12月5日水曜日
  53. 53. DOMオブジェクトに強依存 例えばjQueryでありがちがコード $(function(){   $("div li .button").click(function(){     $("div .contents").html("<span>"+$(this).data("mydata")+"</span>");   }) }) DOMに依存することで発生する問題点 DOM要素が存在しないと実行できない DOM操作に対する副作用の検証(アサー ション)が大抵のケースで非常に難しい UIに伴って変更されやすいHTML構造に依 存してしまう(上記ではセレクター部分) 12年12月5日水曜日
  54. 54. DOMオブジェクトに強依存 問題に対するアプローチ DOMに依存しない部分を分離する $(function(){   $("#button").click(function(){     clickHandler($("#contents"), $(this).data("mydata"));   }) }) function clickHandler(elm, data) {   elm.html("<span>"+data+"</span>"); } DOM操作の振る舞いのみをテストする it("should call html() of passed element", function() {   var fakeObj = {html: jasmine.createSpy()};   clickHandler(fakeObj, "hoge");   expect(fakeObj.html).toHaveBeenCalledWith("<span>hoge</span>"); }); 可能であればHTML構造に依存しない   セレクタ(idセレクタなど)に変更 12年12月5日水曜日
  55. 55. host object/DOMへの 依存を分離した設計で Nodeなどの別環境でも ユニットテストが書ける 12年12月5日水曜日
  56. 56. TDDや自動テストで 活用できる各種ツール 12年12月5日水曜日
  57. 57. コマンドライン環境 (CLI) 12年12月5日水曜日
  58. 58. CLIでTDDする動機 ブラウザ実行での コード修正→保存→アプリ ケーション切替→ブラウザ再読み込み、この 手順が煩雑 ブラウザ実行ではテスト全体の実行と結果確 認が自動化されていない つまり、このままではJenkinsなどのCI環 境に組み込みづらい 12年12月5日水曜日
  59. 59. CLIを持つ主なJS処理系 SpiderMonkey C言語実装、Mozillaで保守 Rhino Java実装、Mozillaで保守 JDK6以降にbundleされている Node.js サーバーサイドJS実行環境 処理系はChromeと同じV8エンジン 同梱されるパッケージ管理のnpmが便利 12年12月5日水曜日
  60. 60. CLIを持つ主なJS処理系 SpiderMonkey C言語実装、Mozillaで保守 Rhino Rhino+Envjsの話をしようと思ったのですが、 Java実装、Mozillaで保守 Node全盛の今ニッチな気配を感じてるのと Envjsがしばらくメンテされてる雰囲気なし... JDK6以降にbundleされている Node.js サーバーサイドJS実行環境 処理系はChromeと同じV8エンジン 同梱されるパッケージ管理のnpmが便利 12年12月5日水曜日
  61. 61. Node.jsのインストール 各プラットフォーム向けのインストーラーを取得 (ただしCygwinは5.10でサポート外...) 12年12月5日水曜日
  62. 62. QUnitをNodeで動かす 12年12月5日水曜日
  63. 63. QUnit + QUnit-TAP QUnit自体に標準出力へテスト結果をレポー トする機能がない npmモジュールとして公開されている  QUnit-TAPを組み合わせるのがオススメ 12年12月5日水曜日
  64. 64. npmインストール パッケージ指定してインストール $ npm install qunitjs $ npm install qunit-tap もしくはpackage.json記述してインストール $ cat package.json {   "name": "sample-of-tdd",   "version": "1.0.0",   "devDependencies": {     "qunitjs": "1.10.0",     "qunit-tap": "1.2.2"   } } $ npm install 12年12月5日水曜日
  65. 65. ソースコードの調整 以下のソースでブラウザ実行していたとする $ tree . !"" node_modules !"" package.json !"" runner.html !"" src #   $"" greeter.js $"" test     $"" greeter_test.js 3 directories, 4 files // src/Greeter.js function Greeter() {   this.greet = "hello"; } // test/greeter module("Greeter"); test("greetがセットされる", function(assert) {   var greeter = new Greeter();   assert.ok(greeter.greet); }); 12年12月5日水曜日
  66. 66. ソースコードの調整 ブラウザ/Node両方で動作するように修正 // src/Greeter.js function Greeter() {   this.greet = "hello"; } if (typeof exports !== "undefined") {   exports.Greeter = Greeter; } // test/greeter_test.js if (typeof exports !== "undefined") {   var QUnit = require("qunitjs");   var qunitTap = require("qunit-tap").qunitTap;   qunitTap(QUnit, console.log, {noPlan: true});   QUnit.init();   QUnit.config.updateRate = 0;   var Greeter = require("../src/Greeter").Greeter; }; QUnit.module("Greeter"); QUnit.test("greetがセットされる", function(assert) {   var greeter = new Greeter();   assert.ok(greeter.greet); }); 12年12月5日水曜日
  67. 67. ソースコードの調整 ブラウザ/Node両方で動作するように修正 // src/Greeter.js function Greeter() {   this.greet = "hello"; } if (typeof exports !== "undefined") { exportsオブジェクトの有無で環境を判別   exports.Greeter = Greeter; } Nodeのモジュール機構に則した形で公開 // test/greeter_test.js if (typeof exports !== "undefined") {   var QUnit = require("qunitjs");   var qunitTap = require("qunit-tap").qunitTap; exportsオブジェクトの有無で環境を判別   qunitTap(QUnit, console.log, {noPlan: true});   QUnit.init(); QUnitの初期化処理とテスト対象コードの   QUnit.config.updateRate = 0; 読み込み   var Greeter = require("../src/Greeter").Greeter; }; QUnit.module("Greeter"); QUnit.test("greetがセットされる", function(assert) { QUnitのグローバル関数は   var greeter = new Greeter(); QUnitオブジェクトから参照   assert.ok(greeter.greet); }); 12年12月5日水曜日
  68. 68. テスト実行 テスト結果がTAP形式で出力される $ node test/greeter_test.js # module: Greeter # test: greetがセットされる ok 1 1..1 proveコマンドを組み合わせることで複数フ ァイルの実行&サマリーも可能 $ prove -e node test/* test/greeter_test.js .. ok All tests successful. Files=1, Tests=1, 1 wallclock secs ( 0.03 usr 0.01 sys + 0.09 cusr 0.01 csys = 0.14 CPU) Result: PASS 12年12月5日水曜日
  69. 69. JasmineをNodeで動かす 12年12月5日水曜日
  70. 70. Jasmine-node Jamine-coreとそれを実行するCLIで構成され るnpmモジュール オプションでJUnit XMLフォーマットで出力 などCLI向けの拡張がいくつかなされている 12年12月5日水曜日
  71. 71. npmインストール コマンドラインツールさえ利用できればよい ので -g オプションでシステムにインストール $ npm install -g jasmine-node ちなみに -g オプションなしでインストール  したモジュールのコマンドラインツールは node_modules/.bin/ 以下に入る $ npm install -g jasmine-node $ tree node_modules/.bin/ node_modules/.bin/ $"" jasmine-node -> ../jasmine-node/bin/jasmine-node 0 directories, 1 file 12年12月5日水曜日
  72. 72. ソースコードの調整 ブラウザ/Node両方で動作するように修正 // src/Greeter.js function Greeter() {   this.greet = "hello"; } if (typeof exports !== "undefined") {   exports.Greeter = Greeter; } // spec/greeter_spec.js if (typeof exports !== "undefined") {   var Greeter = require("../src/greeter").Greeter; }; describe("Greeter", function() {   it("greetがセットされる", function() {     var greeter = new Greeter();     expect(greeter.greet).toBeDefined();   }); }); 12年12月5日水曜日
  73. 73. ソースコードの調整 ブラウザ/Node両方で動作するように修正 // src/Greeter.js function Greeter() {   this.greet = "hello"; } テスト対象コードはQUnitと同じ修正 if (typeof exports !== "undefined") {   exports.Greeter = Greeter; } // spec/greeter_spec.js if (typeof exports !== "undefined") { テスト対象コードのrequire()を追加   var Greeter = require("../src/greeter").Greeter; それ以外はブラウザ実行と同様でOK }; (jasmine-nodeが解決してくれている) describe("Greeter", function() {   it("greetがセットされる", function() {     var greeter = new Greeter();     expect(greeter.greet).toBeDefined();   }); }); 12年12月5日水曜日
  74. 74. スペック実行 スペック結果が出力される $ jasmine-node spec . Finished in 0.014 seconds 1 test, 1 assertion, 0 failures jasmine-nodeの引数にはディレクトリを指定 ディレクトリ以下の全スペックファイル (*spec.jsにマッチするファイル)を全て 実行してくれる 12年12月5日水曜日
  75. 75. CLI環境での実行 QUnit、Jasmineともにブラウザ上で実行し たソースからテスト(スペック)記述は変更せず に最小限の修正で実行することが可能 しかしまだ、Host ObjectやDOMに依存しな いコードしかCLI環境で実行できない Node上でDOMを実装したモジュールを利用 してCLI環境でテスト可能なコードを増やす 12年12月5日水曜日
  76. 76. Node+jsdomを利用した DOM依存コードの実行 12年12月5日水曜日
  77. 77. jsdomとは? W3CのDOMをJavaScriptで実装したライブ ラリ(npmモジュール) リモートのHTML/XMLやローカルファイル、 文字列をパースしてDOMオブジェクトを作成 これ使えばWebスクレイピングなど簡単 require("jsdom").env(   "http://www.mapion.co.jp",   ["http://code.jquery.com/jquery.js"],   function (errors, window) {     var alt = window.$("h1 img").attr("alt");     console.log(alt); // 地図検索マップ マピオン   } ); 12年12月5日水曜日
  78. 78. npmインストール パッケージ指定してインストール $ npm install jsdom もしくはpackage.json記述してインストール $ cat package.json {   "name": "sample-of-tdd",   "version": "1.0.0",   "devDependencies": {     "qunitjs": "1.10.0",     "qunit-tap": "1.2.2",     "jsdom": "0.2.19"   } } $ npm install 12年12月5日水曜日
  79. 79. どう利用するか? jasmine-nodeにはスペック実行ディレクトリ にある「*helpers.js」を読み込んでくれるの で、そこに以下ヘルパー関数を定義 // spec/spec_helpers.js var jsdom = require("jsdom"); global.init_window = function(opt, callback) {   var html = '<html><body></body></html>';   jsdom.env((opt && opt.html) || html, function(errors, window) {     global.window = window;     global.document = window.document;     callback(errors);   }); }; 12年12月5日水曜日
  80. 80. ヘルパー関数の利用 beforeEachで初期化処理を走らせれば、初期 化されたwindowとdocumentがグローバルに 作成される // spec/jsdom_spec.js describe("jsdomを利用する", function() {   beforeEach(function(done) {     init_window({       html: '<html><body><div id="hoge">bar</div></body></html>'     }, done);   });   it("documentオブジェクトが利用可能", function() {     expect(document.getElementById("hoge").innerHTML).toEqual("bar");   }); }); 参考:https://github.com/mizchi/sample-node-client-test 12年12月5日水曜日
  81. 81. jsdom利用の留意点 windowオブジェクトにはXMLHttpRequest なども定義されており、ほとんどブラウザ しかし、全ての振る舞いが本当のブラウザ上 オブジェクトと同一である保証はない 個人的にはPhantomJSを利用するケースのほ うが多い 12年12月5日水曜日
  82. 82. ヘッドレスブラウザ (PhantomJS) 12年12月5日水曜日
  83. 83. PhantomJSとは? GUIのない(ヘッドレスな)ブラウザ    JSスクリプトファイルで操作する QtWebKitをベースに作られているため HTML5/CSS3といったモダンブラウザの機 能は実装されている 内部でレンダリングは実行されている    API経由で画面キャプチャも取得できる var page = require("webpage").create(); page.open("http://www.mapion.co.jp/", function(state) {   page.render("mapion.png"); // カレントディレクトリに出力   phantom.exit(); }); 12年12月5日水曜日
  84. 84. インストール Windows/MacOSX/Linux向けのバイナリを インストールすれば利用可能 12年12月5日水曜日
  85. 85. ユースケース QUnitやJasmineによるテスト実行HTMLの Test Runner Webページのスクリーンキャプチャツール ユーザー操作をエミュレートしたシナリオテ ストの実行 ページリソース(js, css, img)全てを含めたネ ットワークモニタリング 12年12月5日水曜日
  86. 86. サンプルコード phantomjsソースツリーに含まれる examples/pizza.js // Find pizza in Mountain View using Yelp var page = require('webpage').create(),     url = 'http://lite.yelp.com/search? find_desc=pizza&find_loc=94040&find_submit=Search'; page.open(url, function (status) {     if (status !== 'success') {         console.log('Unable to access network');     } else {         var results = page.evaluate(function() {             var list = document.querySelectorAll('span.address'), pizza = [], i;             for (i = 0; i < list.length; i++) {                 pizza.push(list[i].innerText);             }             return pizza;         });         console.log(results.join('n'));     }     phantom.exit(); }); 12年12月5日水曜日
  87. 87. サンプルコード phantomjsソースツリーに含まれる examples/pizza.js // Find pizza in Mountain View using Yelp var page = require('webpage').create(),     url = 'http://lite.yelp.com/search? find_desc=pizza&find_loc=94040&find_submit=Search'; 単一のページを読み込んでいるブロック page.open(url, function (status) {     if (status !== 'success') {         console.log('Unable to access network');     } else {         var results = page.evaluate(function() {             var list = document.querySelectorAll('span.address'), pizza = [], i;             for (i = 0; i < list.length; i++) {                 pizza.push(list[i].innerText);             }             return pizza;         });         console.log(results.join('n'));     }     phantom.exit(); }); 12年12月5日水曜日
  88. 88. サンプルコード phantomjsソースツリーに含まれる examples/pizza.js // Find pizza in Mountain View using Yelp var page = require('webpage').create(),     url = 'http://lite.yelp.com/search? find_desc=pizza&find_loc=94040&find_submit=Search'; ページ内のコンテキストで実行しているブロック page.open(url, function (status) {     if (status !== 'success') { (セキュリティ上の理由で別コンテキスト)         console.log('Unable to access network'); DOMツリーから情報を取得している     } else {         var results = page.evaluate(function() {             var list = document.querySelectorAll('span.address'), pizza = [], i;             for (i = 0; i < list.length; i++) {                 pizza.push(list[i].innerText);             }             return pizza;         });         console.log(results.join('n'));     }     phantom.exit(); }); 12年12月5日水曜日
  89. 89. サンプルコード phantomjsソースツリーに含まれる examples/pizza.js // Find pizza in Mountain View using Yelp var page = require('webpage').create(),     url = 'http://lite.yelp.com/search? find_desc=pizza&find_loc=94040&find_submit=Search'; page.open(url, function (status) {     if (status !== 'success') {         console.log('Unable to access network');     } else {         var results = page.evaluate(function() {             var list = document.querySelectorAll('span.address'), pizza = [], i;             for (i = 0; i < list.length; i++) {                 pizza.push(list[i].innerText);             }             return pizza;         });         console.log(results.join('n')); 取得した情報を標準出力して     }     phantom.exit(); ブラウザを終了 }); 12年12月5日水曜日
  90. 90. どうTDDで利用するか? Test Runner QUnitやJasmineの実行HTMLを開く テスト実行を待つ 結果HTMLをスクレイピング PhantomJS実行コンテキストで結果出力 GitHubページに各フレームワーク毎に作成さ れているTest Runnerが紹介されている https://github.com/ariya/phantomjs/wiki/Headless-Testing 12年12月5日水曜日
  91. 91. PhantomJS QUnit QUnitにbuilt-inされているjsが利用できる $ phantomjs node_modules/qunitjs/addons/phantomjs/runner.js qunit.html Took 22ms to run 20 tests. 20 passed, 0 failed. 12年12月5日水曜日
  92. 92. pros/cons pros ブラウザそのもの HTML5/CSS3などモダンな実装が動く WebKitなのでスマートフォンの標準ブラウ ザに近い挙動を期待できる cons WebKit以外のブラウザがターゲットの場 合には積極的に利用できない 12年12月5日水曜日
  93. 93. jasmine-gem 12年12月5日水曜日
  94. 94. jasmine-gemとは? ブラウザ実行を楽にするヘルパースクリプト (SpecRunner.htmlの作成が不要) コマンドラインからブラウザによるテスト実 行をサポート 仕組みはWebDriver railsコマンドのサポート 12年12月5日水曜日
  95. 95. インストール gemでインストール $ gem install jasmine -v 1.3.0 12/3にリリースされたv1.3.1がぶっ壊れて いる?っぽいので v1.3.0 を指定 初期化 rails3プロジェクトの場合 $ rails g jasmine:install railsじゃないプロジェクトの場合 $ jasmine init 12年12月5日水曜日
  96. 96. initコマンドの出力 $ jasmine init $ tree . . !"" Rakefile !"" public #   $"" javascripts #   !"" Player.js #   $"" Song.js $"" spec     $"" javascripts         !"" PlayerSpec.js         !"" helpers         #   $"" SpecHelper.js         $"" support             $"" jasmine.yml 6 directories, 6 files 12年12月5日水曜日
  97. 97. initコマンドの出力 $ jasmine init $ tree . . !"" Rakefile !"" public #   $"" javascripts #   !"" Player.js #   $"" Song.js standalone版の初期状態と同じ $"" spec サンプルリソース群が出力されている     $"" javascripts         !"" PlayerSpec.js         !"" helpers         #   $"" SpecHelper.js         $"" support             $"" jasmine.yml 6 directories, 6 files 12年12月5日水曜日
  98. 98. initコマンドの出力 $ jasmine init $ tree . . Rakefileとjasmine.ymlがstatdalone版に !"" Rakefile 含まれていなかったリソース !"" public #   $"" javascripts #   !"" Player.js #   $"" Song.js $"" spec     $"" javascripts         !"" PlayerSpec.js         !"" helpers         #   $"" SpecHelper.js         $"" support             $"" jasmine.yml 6 directories, 6 files 12年12月5日水曜日
  99. 99. Rakeタスクの実行 rake -T コマンドで確認 $ rake -T rake jasmine # Run specs via server rake jasmine:ci # Run continuous integration tests rake jasmineでサーバーが起動、表示された URLにアクセスするとテスト実行できる $ rake jasmine your tests are here:   http://localhost:8888/ ポート指定は環境変数JASMINE_PORT $ JASMINE_PORT=1234 rake jasmine your tests are here:   http://localhost:1234/ 12年12月5日水曜日
  100. 100. 読み込むjsの設定 jasmine.ymlで指定可能、ルールや書き方はコ メントに記載されている # src_files # # Return an array of filepaths relative to src_dir to include before jasmine specs. # Default: [] # # EXAMPLE: # # src_files: # - lib/source1.js # - lib/source2.js # - dist/**/*.js # src_files:     - public/javascripts/**/*.js # stylesheets # : (省略) : 12年12月5日水曜日
  101. 101. $ rake jasmine:ci WebDriverを利用してブラウザ実行も自動化 $ rake jasmine:ci デフォルトではFirefoxが起動 他のブラウザ起動は環境変数 JASMINE_BROWSERで指定を行う $ JASMINE_BROWSER=chrome rake jasmine:ci 指定可能値 ie, chrome, android, iphone, opera see: https://github.com/vertis/selenium-webdriver/blob/master/lib/selenium/webdriver/common/driver.rb#L25 chrome, android, iphone, operaのdriver 詳細はSeleniumのwikiページにご参照 12年12月5日水曜日
  102. 102. 複数ブラウザで実行する 自動テストツール 12年12月5日水曜日
  103. 103. 自動テストツール/テストランナー Browser Capturing Unit Testing Automation Browser そもそもユニットテスト用 Selenium ○ - - ではない ユニットテスト + JsTestDriver - ○ ○ ブラウザキャプチャ ユニットテスト + BusterJS - ○ ○ ブラウザキャプチャ Node製 先月(2012/11)公開された Testacular - - ○ ブラウザキャプチャのみ Node製 12年12月5日水曜日
  104. 104. ブラウザキャプチャ? サーバーにブラウザを接続させコネクション を維持、サーバー側でコマンドを実行するこ とでテスト実行と結果サマリーを複数ブラウ ザで一気に行う方法 正式名称は知りません 12年12月5日水曜日
  105. 105. Testacularを使う 先月末(2012/11)にGoogleからオープンソー ス化して公開されたばかり! Node製でSoket.ioを利用してブラウザキャプ チャを行うシンプルなツール ユニットテストは含まれていない、既存のテ スト資産(Jasmine, Mochaなど)を活用する 12年12月5日水曜日
  106. 106. インストール インストールとコマンドラインオプションの 確認 $ npm install -g testacular $ testacular --help Testacular - Spectacular Test Runner for JavaScript. Usage: testacular <command> Commands: start [<configFile>] [<options>] Start the server / do single run. init [<configFile>] Initialize a config file. run [<options>] Trigger a test run. Run --help with particular command to see its description and available options. Options: --help Print usage and options. --version Print current version. 12年12月5日水曜日
  107. 107. 前準備 テストリソースをjasmine-gemで用意 $ jasmine init $ tree . . !"" Rakefile !"" public #   $"" javascripts #   !"" Player.js #   $"" Song.js $"" spec     $"" javascripts         !"" PlayerSpec.js         !"" helpers         #   $"" SpecHelper.js         $"" support             $"" jasmine.yml 6 directories, 6 files 12年12月5日水曜日
  108. 108. 設定ファイルの作成 initコマンドで対話的に作成してくれる $ testacular init Which testing framework do you want to use ? Press tab to list possible options. Enter to move to the next question. > jasmine Do you want to capture a browser automatically ? Press tab to list possible options. Enter empty string to move to the next question. > Chrome > Firefox > Safari > Which files do you want to test ? You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js". Enter empty string to move to the next question. > public/**/*.js > spec/**/*.js > Any files you want to exclude ? You can use glob patterns, eg. "**/*.swp". Enter empty string to move to the next question. > Do you want Testacular to watch all the files and run the tests on change ? Press tab to list possible options. > yes Config file generated at "/Users/kozy/js-dev/testacular/testacular.conf.js". 12年12月5日水曜日
  109. 109. 設定ファイルの作成 initコマンドで対話的に作成してくれる $ testacular init Which testing framework do you want to use ? どのテストフレームワークを利用するか? Press tab to list possible options. Enter to move to the next question. > jasmine デフォルトでJasmineかMochaが選択可 Do you want to capture a browser automatically ? Press tab to list possible options. Enter empty string to move to the next question. > Chrome > Firefox サーバー起動時に接続するブラウザ > Safari 起動後に手動で接続することも可能 > Which files do you want to test ? You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js". Enter empty string to move to the next question. > public/**/*.js テスト実行HTMLに読み込むjsファイル > spec/**/*.js > globパターンで指定可能 Any files you want to exclude ? You can use glob patterns, eg. "**/*.swp". Enter empty string to move to the next question. > 逆に読み込まないjsファイルを指定 Do you want Testacular to watch all the files and run the tests on change ? Press tab to list possible options. > yes ファイル更新を検知して再実行するか Config file generated at "/Users/kozy/js-dev/testacular/testacular.conf.js". 12年12月5日水曜日
  110. 110. 設定ファイルを修正 何故かパス設定がうまく動かない... basePathを修正する   1 // Testacular configuration   2 // Generated on Wed Dec 05 2012 23:01:06 GMT+0900 (JST)   3   4   5 // base path, that will be used to resolve files and exclude   6 basePath = '../../../../..';   7   8   9 // list of files / patterns to load in the browser  10 files = [  11 JASMINE,  12 JASMINE_ADAPTER,  13 'public/**/*.js',  14 'spec/**/*.js'  15 ]; : 12年12月5日水曜日
  111. 111. 設定ファイルを修正 何故かパス設定がうまく動かない... basePathを修正する   1 // Testacular configuration   2 // Generated on Wed Dec 05 2012 23:01:06 GMT+0900 (JST)   3   4   5 // base path, that will be used to resolve files and exclude 内部ではrequire('path').resolve(basePath, files[i])で   6 basePath = '';   7 解決するため正しいパスが得られない... 空文字列に変更   8   9 // list of files / patterns to load in the browser  10 files = [  11 JASMINE,  12 JASMINE_ADAPTER,  13 'public/**/*.js',  14 'spec/**/*.js'  15 ]; : 12年12月5日水曜日
  112. 112. 実行! 以下コマンドでサーバーが起動 $ testacular start 設定ブラウザも起動しキャプチャされる もちろん手動で接続してキャプチャさせる ことも可能(スマホブラウザなど) 読み込みファイルの更新検知、キャプチャ済 みブラウザのリロード、runコマンドの送信で ユニットテストが各ブラウザで自動実行 12年12月5日水曜日
  113. 113. 使ってみた感じ 設定ファイルの自動生成など、導入する面倒 くささがまったくない テスト実行がかなり早い、ファイル更新での 自動実行もサクサク動く よくたびたびtestacular経由で起動した Chromeが終了ミス?って親なしプロセスに まだ粗い感じもあるが、かなり使えるツール なのでは? 12年12月5日水曜日
  114. 114. CI (Jenkins) 12年12月5日水曜日
  115. 115. jarのダウンロード 12年12月5日水曜日
  116. 116. jarから直接起動 ちゃんと運用する時はTomcatなどアプリケー ションコンテナにデプロイしてください 以下コマンドで8080ポートで起動 $ java -jar jenkins.war 12年12月5日水曜日
  117. 117. http://localhost:8080/ 新規ジョブ作成はこっち システムの設定はここ Gitプラグインをまず入れる 12年12月5日水曜日
  118. 118. Gitプラグイン取得 12年12月5日水曜日
  119. 119. Gitプラグイン取得 チェックを入れて再起動 12年12月5日水曜日
  120. 120. 再起動して新規ジョブ作成 フリースタイルでOK 12年12月5日水曜日
  121. 121. プロジェクト設定(1) テストなのでローカルパスで 12年12月5日水曜日
  122. 122. プロジェクト設定(2) ビルド手順にテスト実行スクリプトを記述 jasmine-nodeのjunitreportはデフォルトで reports以下に結果を出力する 12年12月5日水曜日
  123. 123. ビルド実行 手動で実行 12年12月5日水曜日
  124. 124. 結果が確認できる 12年12月5日水曜日
  125. 125. ビルド実行URL 以下URLでビルドが実行される [プロジェクトURL]/build?delay=0sec Gitならコミットフックを仕込むと幸せになれる $ echo 'curl "http://localhost:8080/job/your_project/build?delay=0sec"'>.git/hooks/pre-commit $ chmod +x .git/hooks/pre-commit 12年12月5日水曜日
  126. 126. まとめ 12年12月5日水曜日
  127. 127. まとめ ブラウザ上で実行するユニットテストツール QUnitとJasmineを紹介しました QUnitとJasmineをベースにTDDで活用でき そうなCLI環境やヘッドレスブラウザの利用方 法を紹介しました ブラウザキャプチャによる複数ブラウザでの ユニットテスト同時実行を紹介しました CIをJenkinsで行うための簡単な設定例を紹介 しました 12年12月5日水曜日
  128. 128. 実は... BusterJSを使えば ブラウザ上でユニットテスト出来ます Nodeでユニットテスト出来ます 複数ブラウザの同時実行も出来ます JenkinsなどでCI導入も可能です BusterJSの万能感がハンパないです 12年12月5日水曜日
  129. 129. 今後特にウォッチしたい BusterJS Testacular 12年12月5日水曜日
  130. 130. 以上 ありがとうございました 12年12月5日水曜日
  • redeeer

    May. 4, 2017
  • yuuyatogashi3

    Sep. 12, 2016
  • KenjiSato2

    Sep. 1, 2016
  • masawamorishita

    Jan. 13, 2016
  • HirokazuUchiyama

    Aug. 30, 2015
  • syamaguchi

    Jun. 1, 2015
  • TakeshiSakurai

    May. 22, 2015
  • oobayashi

    Apr. 20, 2015
  • 99corps

    Mar. 9, 2015
  • s-mori

    Jan. 14, 2015
  • TomoeNakada

    Oct. 29, 2014
  • msyskz3

    Oct. 13, 2014
  • kensonoda777

    Sep. 10, 2014
  • mm3343

    Sep. 9, 2014
  • HiroshiShiobara

    Aug. 26, 2014
  • ghrgc

    Jul. 11, 2014
  • muyuu

    Jun. 1, 2014
  • nagasakihamu

    May. 29, 2014
  • hiromitsuuuuu

    May. 15, 2014
  • tzm_freedom

    May. 12, 2014

Views

Total views

36,475

On Slideshare

0

From embeds

0

Number of embeds

2,127

Actions

Downloads

0

Shares

0

Comments

0

Likes

172

×