Loading...

js テスト放浪記

和田 卓人 (a.k.a id:t-wada or @t_wada)

Press key to advance.

Slides controls, press:

  • and to move around.
  • Ctrl/Command and + or - to zoom in and out if slides don’t fit.
  • T to change the theme.
  • H to toggle syntax highlight.

自己紹介

  • 名前 : 和田 卓人(わだ たくと)
  • ブログ : id:t-wada
  • Twitter : @t_wada
  • github / facebook : twada
  • タワーズ・クエスト株式会社 取締役社長

./../../images/TQ_LOGO_SMALL.png

gihyo.jpの連載

  • 「動画で解説」和田卓人の"テスト駆動開発"講座
  • http://gihyo.jp/dev/serial/01/tdd
  • 全20回すべて動画付き解説
  • ニコニコ動画でも見れます

./../../images/gihyo-tdd-icon.png

イベントや参加団体等

  • Developers Summit (通称デブサミ)
  • java-ja
  • asakusa.rb
  • xUnit Test Patterns 読書会
  • TDD Boot Camp
  • Shibuya.js (NEW!)

プログラマが知るべき97のこと

プログラマが知るべ 97 のこ

#97prog_ja

普段やっていること

  • 商用 Rails プラグインの開発
  • コンサルティング
  • TDD の啓蒙
  • Twitter, facebook

おわび

  • 今日はスーツですみません
  • たいへん地味な発表になりそうです
  • 派手なのはyukobaさんがやってくれるはず!

よろしくお願いします

テストフレームワーク遍歴

  • 2007/??/?? : JsUnit
  • 2008/05/15 : SimpleTest.js(MochiKit付属)
  • 2008/06/22 : QUnit(の前身)
  • 2008/08/04 : MochiKit/Test.js
  • 2009/02/03 : JSpec
  • 2009/06/09 : unittest.js
  • 2009/11/05 : QUnit
  • 2009/12/24 : QUnit + QUnit-TAP
  • 2011/02/24 : QUnit + QUnit-TAP + phantomjs (NEW!)

js テストの諸要素

  • テスト書法
  • 起動方法
  • 実行母体
  • 出力書式

テスト書法あれこれ

  • xUnit 系
  • Test::Simple 系
  • Spec 系
  • その他 (expectations等)

xUnit 系書法(unittest.js)

new Test.Unit.Runner(
    {
        'test stringToBoolean("true") => true' : function(){
            this.assertEqual(true, this.conv.stringToBoolean("true"));
        },
        'test stringToBoolean("false") => false' : function(){
            this.assertEqual(false, this.conv.stringToBoolean("false"));
        },
        'test stringToBoolean("FALSE") => false' : function(){
            this.assertEqual(false, this.conv.stringToBoolean("FALSE"));
        },
        'test stringToBoolean("1") => true' : function(){
            this.assertEqual(true, this.conv.stringToBoolean("1"));
        },
        'test stringToBoolean("0") => false' : function(){
            this.assertEqual(false, this.conv.stringToBoolean("0"));
        }
    }
);

Test::Simple 系書法(MochiKit/Test.js)

if (typeof(chapter3) === 'undefined') { chapter3 = {}; }
chapter3 = function(t) {
    var stooge = {
        "first-name": "Joe",
        "last-name": "Howard"
    };

    print("# p.21 Retrieval");

    t.is(stooge["first-name"], "Joe");
    t.ok(stooge["middle-name"] === undefined);
    t.ok(stooge["FIRST-NAME"] === undefined);
};

Spec 系書法(JSpec)

describe 'ShoppingCart'
  before_each
    cart = new ShoppingCart
  end
  
  describe 'addProduct'
    it 'should add a product'
      cart.addProduct('cookie')
      cart.addProduct('icecream')
      cart.should.have 2, 'products'
    end
  end
end

JavaScript ……なのか……!?

出力書式

  • Web 画面
  • progress (….F…F..)
  • TAP (Test Anything Protocol)
  • その他 (Spec description 等)

Web 画面出力(QUnit)

./../../images/qunit_web.png

progress 出力

% rake test
Started tests in Firefox.
......................
Finished in 34.785718 seconds.
349 tests, 2238 assertions, 0 failures, 0 errors.

Started tests in Safari.
......................
Finished in 12.315652 seconds.
358 tests, 2299 assertions, 0 failures, 0 errors.
%

TAP 出力

1..16
# module: math module
# test: add
ok 1
ok 2
ok 3 - passing 3 args
ok 4 - just one arg
ok 5 - no args
not ok 6 - expected: 7 result: 1
not ok 7 - with message, expected: 7 result: 1

https://github.com/twada/qunit-tap

実行母体

  • 実ブラウザ
  • ブラウザといってもいろいろある(IEとかIEとか)
  • headless env (画面の無いブラウザやエミュレータライブラリ)

headless env

  • テストは CUI でやりたい
  • いくつか function が足りない? 気合で足すんだよ!
  • env-js
  • xmlw3cdom.js
  • phantomjs (NEW!)

PhantomJS

  • "PhantomJS is a minimalistic, headless, WebKit-based, JavaScript-driven tool"
  • 限りなく本物っぽい!

./../../images/lemmling_Cartoon_ghost.png

テストフレームワーク遍歴まとめ

テスト書法起動方法実行母体出力書式依存
JsUnitxUnit系ブラウザブラウザ画面無し
SimpleTest.js(MochiKit)Test::Simple系ブラウザブラウザ画面MochiKit
QUnit(の前身)Test::Simple系ブラウザブラウザ画面jQuery
MochiKit/Test.jsTest::Simple系CUIspidermonkey or rhinoTAPMochiKit
JSpecSpec系ブラウザブラウザ画面無し
unittest.jsxUnit系CUI(rake)(複数の)ブラウザprogressprototype.js
QUnitTest::Simple系ブラウザブラウザ画面無し
QUnit + QUnit-TAP + spidermonkeyTest::Simple系CUIspidermonkey + xmlw3cdom.jsTAP無し
QUnit + QUnit-TAP + phantomjsTest::Simple系CUIphantomjsTAP無し

放浪から学んだこと

  • テスト書法は自分の好きなものを
  • 実行母体は不安とスピードの綱引き
  • 出力書式はオレオレではなく一般的なものを
  • 依存は無いほうが良い(引っ越しやすい)

いまのスタイルの理由

UNIXという考え方

ソフトウェアのテコの原理

  • QUnit から TAP を出したいので QUnit-TAP を書いた
  • テコに乗ると、突然世界が開けてきた
  • それは TAP が Unix の流儀に倣っているから
  • 単機能
  • 行指向
  • フィルタ

きのこ83: UNIXツールを友にする

./../../images/017_Diomidis_Spinellis.jpg 既存のツールがどれも自分の希望に合わないという場合も、UNIXツールなら簡単に拡張ができます。いくつか 簡単なルール を守ってプログラムを書けばいいのです(言語はどれでもかまいません)。そのルールとは、 シングルタスクのプログラムにする こと、 標準入力からテキスト行としてデータを読み込む こと、 実行結果は標準出力に書き込み 、その際ヘッダなどの余計な飾りはつけない こと、です。 ツールの 動作に影響するパラメータはすべてコマンドラインに指定 するようにしましょう。これらのルールを守れば、「この世界とそこにあるものは すべて君のもの」、必要十分な機能を持ち、応用範囲の広いツールができるでしょう。

変化に対応する

  • 多くの場合はテスト対象の変化
  • でも js には「テスト 方法 の変化」もある
  • 「うまくいっている」しくみに倣う。 Unix, REST
  • ソフトウェアのテコの原理を理解する

ツールとデモ

phantomjs のインストール

Ubuntu 10.04 の場合

$ sudo aptitude install qt4-qmake libqtwebkit2.2-cil libqt4-dev
$ git clone git://github.com/ariya/phantomjs.git
$ cd phantomjs
$ qmake-qt4 && make

他の OS の場合は http://code.google.com/p/phantomjs/wiki/BuildInstructions を参照

run_qunit.js

phantomjs なら、既存のテスト用 html に全く変更を加えず CUI で走らせることができる

if (phantom.state.length === 0) {
    if (phantom.args.length === 0 || phantom.args.length > 2) {
        console.log('Usage: run-qunit.js URL');
        phantom.exit();
    } else {
        phantom.state = 'run-qunit';
        phantom.open(phantom.args[0]);
    }
} else {
    setInterval(function() {
        var el = document.getElementById('qunit-testresult');
        if (phantom.state !== 'finish') {
            if (el && el.innerText.match('completed')) {
                phantom.state = 'finish';
                try {
                    failed = el.getElementsByClassName('failed')[0].innerHTML;
                } catch (e) {
                }
                phantom.exit((parseInt(failed, 10) > 0) ? 1 : 0);
            }
        }
    }, 100);
}

TAP 出力スクリプト

phantomjs の console.log に QUnit-TAP から TAP 出力し、標準出力に出す

#!/bin/sh
# to run: ./test.sh or prove test.sh

URL=file://$PWD/public/test.html
phantomjs run_qunit.js $URL | sed -e "s%^$URL\:[0-9]* %%g"

出した TAP 出力を prove に食わせる

$ prove test.sh
test.sh .. ok     
All tests successful.
Files=1, Tests=682,  7 wallclock secs ( 0.17 usr  0.01 sys +  7.06 cusr  0.14 csys =  7.38 CPU)
Result: PASS
$ 

autotest っぽいもの

inotifywait でファイルの変更を監視し、変更があったら phantomjs でテストを走らせる

#!/bin/sh

while R=$(inotifywait -r -e modify public/test public/lib)
do
    TOUCHED_FILE=$(echo $R|awk '{print $3}')
    URL=http://localhost:4567/tap/$TOUCHED_FILE
    phantomjs run_qunit.js $URL \
        | sed -e "s%^$URL\:[0-9]* %%g" \
        | tee js_test.log | grep '^not ok' > /dev/null
    if [ $? -eq 0 ]; then
        notify-send -u critical -t 1000 \
            --icon=$PWD/fail.png 'FAILED'
    else
        notify-send -u normal -t 1000 \
            --icon=$PWD/pass.png 'SUCCEEDED'
    fi
done

(速度を出すために Sinatra でサーバを立てて、テストコンテクストを絞っています)

カバレッジ測定(jscoverage)

jscoverage のインストール

$ wget http://siliconforks.com/jscoverage/download/jscoverage-0.5.1.tar.bz2
$ tar jxf jscoverage-0.5.1.tar.bz2 
$ cd jscoverage-0.5.1/
$ ./configure 
$ make
$ sudo paco -D make install

jscoverage のデモサイト

しかし、欲しいのはそういうのではない

  • そこで –no-browser オプション(隠しオプション?)ですよ
jscoverage --no-highlight --no-browser $SRC $DEST
  • カバレッジ測定コードが埋め込まれたコードを生成する
  • カバレッジは _$jscoverage というグローバル変数に格納される
  • 後は好きに料理できる
  • 大事なのは、カバレッジを 自分へのフィードバック とすること
  • プログラミングツールとしてのカバレッジ

カバレッジ測定スクリプト

#!/bin/sh
# to run: ./cov.sh > coverage_stats.csv

SRC=public/lib
DEST=public/instrumented

rm -rf $DEST
jscoverage --no-highlight --encoding=UTF-8 --no-browser $SRC $DEST
cat $SRC/test.html \
    | sed -e 's%../lib/%../instrumented/%g' > $SRC/inst.html

URL=file://$PWD/$SRC/inst.html

phantomjs run_jscov.js $URL | grep '^#cov#' \
    | awk 'BEGIN{ "pwd" | getline vpwd }{print vpwd "/'$SRC'/" $2}'

CSV形式でカバレッジデータが出るので、好きに料理する(私の場合はelispを書いてEmacs連携)

lint系ツール

  • js2-mode.el
  • gjslint + flymake (時間があれば)

まとめ

  • js テストの諸要素と基本戦略
  • 変化の中で、この先生きのこるには
  • ソフトウェアのテコの原理

まとめ2 : ツール

  • headless env
  • TAP producer
  • autotest
  • coverage analysis
  • lint

君の銀河もきっと輝く