2012年12月12日水曜日

モダンな TypeScript テスト環境

「これが現代のテスト環境や、どやぁ。」 と @vvakame が用意してくれました。 https://github.com/vvakame/typescript-project-sample

初心者の私にはたくさんのライブラリ(フレームワーク?)が入っててよくわからなかったので、それぞれの立ち位置を教えてもらったのでまとめておきます。


PhantomJS

PhantomJS は JavaScript の API も利用できる headless な WebKit です。実際の描画処理を行わないので速いという特徴があります。また、DOM 操作、CSS セレクタ、JSON、Canvas、SVG などいくつかの web 標準をネイティブでサポートしています。
JavaScript のエンジンは JavaScriptCore らしいです。

実際に Web ページにアクセスして、レンダリング結果の画面のキャプチャをとったり、ページのタイトルを取ってきたり、DOM の値を取ってきたり、いろいろできます。

Quick Start を見ると何ができるかが分かると思います。

例えば、

evaluate.js

  1. var url = "http://www.google.com";  
  2. var page = require("webpage").create();  
  3. page.open(url, function(status) {  
  4.  var title = page.evaluate(function() {  
  5.   return document.title;  
  6.  });  
  7.  console.log("Page title is " + title);  
  8.  phantom.exit();  
  9. });  
というコードを書いて実行すると、ページのタイトルを取れます。

$ phantomjs evaluate.js
Page title is Google
サンプルもたくさん用意されています。



Jasmine

Jasmine は JavaScript コードをテストするためのフレームワークです。 使い方は Introduction のコードを見るのがわかりやすいです。その他の情報は Wiki にあります。

最新版は Jasmine 1.3.1 で https://github.com/pivotal/jasmine/downloads からダウンロードできます。

jasmine-standalone-1.3.1.zip を展開すると、次のような構成になっています。

lib/
spec/
src/
SpecRunner.html
lib/ には jasmine.js や jasmine-html.js が入っています。

SpecRunner.html は jasmine を使ったテストのサンプルです。
基本的には、この SpecRunner.html を自分のプロジェクトに合わせて変えることでテストをセットアップできます。

SpecRunner.html で Player.js, Song.js が指定されている部分
  1. <!-- include source files here... -->  
  2. <script type="text/javascript" src="src/Player.js"></script>  
  3. <script type="text/javascript" src="src/Song.js"></script>  
にテストしたい元コードを、
SpecHelper.js, PlayerSpec.js が指定されている部分に
  1. <script type="text/javascript" src="spec/SpecHelper.js"></script>  
  2. <script type="text/javascript" src="spec/PlayerSpec.js"></script>  
テストコードを置きます。

下の方の Javascript のコードは window.onload でテストを実行し、結果を html に表示するための部分なので変更はいらいないです。
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  2.   "http://www.w3.org/TR/html4/loose.dtd">  
  3. <html>  
  4. <head>  
  5.   <title>Jasmine Spec Runner</title>  
  6.   
  7.   <link rel="shortcut icon" type="image/png" href="lib/jasmine-1.3.1/jasmine_favicon.png">  
  8.   <link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.1/jasmine.css">  
  9.   <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>  
  10.   <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script>  
  11.   
  12.   <!-- include source files here... -->  
  13.   <script type="text/javascript" src="src/Player.js"></script>  
  14.   <script type="text/javascript" src="src/Song.js"></script>  
  15.   
  16.   <!-- include spec files here... -->  
  17.   <script type="text/javascript" src="spec/SpecHelper.js"></script>  
  18.   <script type="text/javascript" src="spec/PlayerSpec.js"></script>  
  19.   
  20.   <script type="text/javascript">  
  21.     (function() {  
  22.       var jasminejasmineEnv = jasmine.getEnv();  
  23.       jasmineEnv.updateInterval = 1000;  
  24.   
  25.       var htmlReporter = new jasmine.HtmlReporter();  
  26.   
  27.       jasmineEnv.addReporter(htmlReporter);  
  28.   
  29.       jasmineEnv.specFilter = function(spec) {  
  30.         return htmlReporter.specFilter(spec);  
  31.       };  
  32.   
  33.       var currentWindowOnload = window.onload;  
  34.   
  35.       window.onload = function() {  
  36.         if (currentWindowOnload) {  
  37.           currentWindowOnload();  
  38.         }  
  39.         execJasmine();  
  40.       };  
  41.   
  42.       function execJasmine() {  
  43.         jasmineEnv.execute();  
  44.       }  
  45.   
  46.     })();  
  47.   </script>  
  48.   
  49. </head>  
  50.   
  51. <body>  
  52. </body>  
  53. </html>  
この SpecRunner.html をブラウザで開くと、このようにテスト結果が表示されます。

Jasmine は JavaScript で構築されているため、Jasmine を実行するには、web page など JavaScript が実行できる環境にいなければいけません。

しかし、この JavaScript を実行するのに毎回ブラウザを立ち上げていたらテストに時間がかかってしまいます。そこで上記の PhantomJS の登場です。

追記 : X Windowとか動いてないLinux機上(大抵のJenkins氏が動いてるサーバはそういう環境)の上で、ブラウザを使ってテストしたいなーというのもある by @vvakame

例えば、
test-jasmine1.js
  1. var url = "file:///home/yanzm/Downloads/jasmine-standalone-1.3.1/SpecRunner.html"  
  2. var page = require("webpage").create();  
  3.   
  4. page.onConsoleMessage = function (msg) {  
  5.     if(msg == "finish-jasmine") {  
  6.         phantom.exit();  
  7.     }  
  8.     else if(msg != "\n") {  
  9.         console.log(msg);  
  10.     }     
  11. };  
  12.   
  13. page.open(url, function(status) {  
  14.     page.evaluate(function() {  
  15.   
  16.         var jasmineEnv = jasmine.getEnv();  
  17.         jasmineEnv.updateInterval = 1000;  
  18.   
  19.         var consoleReporter = new jasmine.ConsoleReporter(  
  20.             function(log) {  
  21.                 console.log(log);  
  22.             },   
  23.             function(runner) {  
  24.                 console.log("finish-jasmine");  
  25.             },   
  26.             false);  
  27.   
  28.         jasmineEnv.addReporter(consoleReporter);  
  29.         jasmineEnv.execute();  
  30.     });  
  31. });  
SpecRunner.html
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  2.   "http://www.w3.org/TR/html4/loose.dtd">  
  3. <html>  
  4. <head>  
  5.   <title>Jasmine Spec Runner</title>  
  6.   
  7.   <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>  
  8.   <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-console.js"></script>  
  9.   
  10.   <!-- include source files here... -->  
  11.   <script type="text/javascript" src="src/Player.js"></script>  
  12.   <script type="text/javascript" src="src/Song.js"></script>  
  13.   
  14.   <!-- include spec files here... -->  
  15.   <script type="text/javascript" src="spec/SpecHelper.js"></script>  
  16.   <script type="text/javascript" src="spec/PlayerSpec.js"></script>  
  17. </head>  
  18.   
  19. <body>  
  20. </body>  
  21. </html>  
(jasmine-console.js の中身は ConsoleReporter.js です。)

のようにすると、コンソールからテストできるようになります。
$ phantomjs test-jasmin1.js 
Started
.
.
.
.
.
Finished in 0.005 seconds
5 specs, 0 failures



grunt

grunt は要は make みたいなものです。

makegrunt
Makefilegrunt.js
make hogegrunt hoge


gruntfile と呼ばれる grunt.js に設定を書いていきます。
Getting Started を見るとなんとなく書き方がわかると思います。

grunt は実行時にカレントディレクトリの grunt.js を見に行くのですが、ない場合は見つけるまで親のディレクトリを遡って探します。普通はプロジェクトリポジトリのルートに置きます。
grunt.js は次の3つの要素からなる JavaScript です。
  • プロジェクトの設定(依存しているライブラリのダウンロードなど)
  • grunt のプラグインやタスクフォルダの読み込み
  • タスクとヘルパー
プロジェクトの設定は grunt.initConfig() で行います。
プラグインやタスクフォルダの指定は grunt.loadTasks() や grunt.loadNpmTasks() で行います。
タスクやヘルパーの指定は grunt.registerTask() で行います。

  1. module.exports = function(grunt) {  
  2.   
  3.   // Project configuration.  
  4.   grunt.initConfig({  
  5.     lint: {  
  6.       all: ['grunt.js''lib/**/*.js''test/**/*.js']  
  7.     },  
  8.     jshint: {  
  9.       options: {  
  10.         browser: true  
  11.       }  
  12.     }  
  13.   });  
  14.   
  15.   // Load tasks from "grunt-sample" grunt plugin installed via Npm.  
  16.   grunt.loadNpmTasks('grunt-sample');  
  17.   
  18.   // Default task.  
  19.   grunt.registerTask('default''lint sample');  
  20.   
  21. };  
各 grunt タスクは、grunt.initConfig() に渡されるオブジェクトで定義されている情報に依存します。
このオブジェクトは JavaScript であって JSON ではないので、プログラムで設定オブジェクトを生成させるようなこともできます。
ビルトインのタスクの設定オブジェクトの詳細は Document の Built-in tasks にあります。


フォルダからタスクやヘルパーを読み込むには
grunt.loadTask(folderName);
を使います。

npm を介して grunt のプラグインを読み込むには
grunt.loadNpmTasks(pluginName)
で行います。


grunt.registerTask() で default タスクを設定するまで、単に grunt を実行してもになにも起こりません。

grunt.registerTask('default', 'hoge_task fuga_task');

のように default タスクを登録すると

$ grunt
$ grunt default

と実行したときに第2引数で指定したタスクが実行されます。
上記の場合

$ grunt hoge_task fuga_task

と実行した場合と同じになります。

grunt.registerTask('test', 'hoge_test fuga_test');

のように、好きな alias を登録できます。
この場合、

$ grunt test

とすれば

$ grunt hoge_test fuga_test

と実行したのと同じになります。

init task(ビルトインのタスク)を見てみると、grunt による project の構成がだいだいわかると思います。



まとめ

@vvakame のプロジェクトの grunt.js では以下のライブラリを使っています。

まとめると、
grunt でディレクトリの clean、typescript や less のコンパイル、jasmine の設定など

jasmine で phantomJS を使ってテスト

結果が JUnit 形式の XML で出力される(出力先は grunt.js で設定)

という流れです。



0 件のコメント:

コメントを投稿