Reports

Twitter: @mtknnktm.

FuncUnitを試してみた

FuncUnitって?

WEBアプリケーションの単体テストフレームワークです.
公式ページ: FuncUnit

基本的にはjQuery風の記法でユーザのアクションを定義し,JavaScript単体テストフレームワーク QUnitでアサートするだけでユーザのアクション(テキスト入力,クリック,ドラッグアンドドロップなど)に対するアプリの動作をテストすることができます.
なので,jQueryQUnitを使ったことがある人であれば比較的簡単にテストコードを書くことができます.また,どちらも使ったことがなくても使う分にはそれほど難しいものでもないので,使ってみることをオススメします.
一回書けば延々ブラウザでポチポチする作業とはおさらばです.

また,もう一つ重要な点としてコマンドラインからテストが実行できるという点です(実行中にFirefoxやIEが立ち上がったりしますが).ということはHudsonと連携して…とか考えると夢が広がりますね.
とか思っていたら,すでに実行に移している方がいました.
java-ja.js #2でLT発表してきた(Envjsを使った単体テスト) - SEの行き着くところ…

FuncUnitは使っていないですが,動機やテスト自動化の構成はFuncUnitと似ている(かつ,本記事よりちゃんと書いてある)ので,JavaScript単体テスト自動化に興味のある方は是非読んでみてください.こちらではHudsonとの連携までしています.

FuncUnitの素

FuncUnitは以下からできています.

  • Selenium: Webアプリのテストを自動化するためのツール.ブラウザ上のユーザの動作を真似てくれます.
  • QUnit: JavaScriptのテストフレームワークQUnitで動作結果を検証します.
  • jQuery: JavaScriptのライブラリ.jQueryでユーザの動作を定義します.
  • EnvJS: JavaScriptで書かれたブラウザやDOM環境の環境をシミュレートしてくれるライブラリ.
  • Syn: ユーザの動作(イベント)をシミュレートしてくれます.

使ってみる

準備

まずは以下からFuncUnitをダウンロードして適当なフォルダに展開してください.
FuncUnit

以降ではテストコードを作成していきます.
ブラウザ上でJavaScriptを動かしたり,ポップアップウインドウを開いたりするのでAPサーバ上に配置しておくのがいいかと思います.

テストコード作成

まず,テスト対象の画面を作ってみます.
名前はmyapp.htmlとします.


<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <title>My Demo App</title>

    <script type='text/javascript' src='./jquery-1.4.2.js'></script>
    <script type='text/javascript'>
      $(function(){
        //ボタンを押すとテキストボックスの内容がdiv要素に書き込まれるだけのアプリケーション
        $('#btn1').click(function(e){
          $('#div1').html($('#txt1').val());
        });
      });
    </script>

  </head>
  <body>
    <h2>FuncUnit Test</h2>
    <input id="txt1" type="text" />
    <input id="btn1" type="button" value="COPY"/>
    <div id="div1"></div>

  </body>
</html>

次にユーザのアクションとそれに対するアサーションを記述します.
名前はfuncunit_test.jsとします.基本的にQUnitです.
jQueryとよく似ていますがセレクタを書く関数が '$' ではなく 'S' になっていることに注意してください.


module("myapp",{
  setup : function(){
    S.open("【myapp.htmlを配置したパス】/myapp.html")
  }
});
test("input Test", function(){
  S("#txt1").type("テスト", function(){
    same(S("#txt1").val(), "テスト","テキストボックスのタイプテスト");
  });

  S("#btn1").click(function(){
    same(S("#div1").text(), "テスト","ボタン押下テスト");
  });
});

  • S("#txt1").type("テスト", function(){…コールバック関数…});

この文がidがtxt1のコンポーネントに「テスト」をタイピングするというユーザの動作を定義しています.
この動作に対してアサートをしたい場合はコールバック関数でQUnitアサーション関数を呼び出します.

  • S("#btn1").click("テスト", function(){…コールバック関数…});

こちらはidがbtn1のコンポーネントをクリックするというユーザの動作を定義しています.
こちらも同様にコールバック関数でQUnitアサーション関数を呼び出してユーザのアクションに対するWEBアプリの動作を検証しています.

QUnit単体ではユーザのアクションを記述するのが,結構面倒なので上記のような直感的かつ簡単な書き方ができるだけでも,結構幸せになれそうです.


最後にFuncUnitの画面を作ります.
名前はfuncunit.htmlとします.ブラウザからテストを実行する場合はこの画面がテスト結果を表示します.
これはほとんどの場合,書き換える必要はないと思います.


<html>
 <head>
  <link rel="stylesheet" type="text/css" href="【FuncUnitを展開したパス】/qunit.css" />
  <title>FuncUnit Test</title>
   <script type='text/javascript' src='【FuncUnitを展開したパス】/funcunit.js'></script>
   <script type='text/javascript' src='funcunit_test.js'></script>
 </head>
 <body>
  <h1 id="qunit-header">FuncUnit Test Suite</h1>
   <h2 id="qunit-banner"></h2>
   <div id="qunit-testrunner-toolbar"></div>
   <h2 id="qunit-userAgent"></h2>
  <ol id="qunit-tests"></ol>
 </body>
</html>

以上で準備は終わりです.
いよいよ実行してみましょう.

ブラウザから実行

上で作った funcunit.html を開いてください.以下のような画面が表示されれば成功です.
見方はQUnitと同じです.
f:id:swarm_of_trials:20110130163851p:image

コンソールから実行

FuncUnitを展開したディレクトリ(envjsがあるディレクトリ)で以下を実行します.
% envjs 【funcunit.htmlを配置したパス】/funcunit.html
以下のようなに出力されれば成功です.
Firefoxの場合
f:id:swarm_of_trials:20110130163852p:image

ちなみに複数のブラウザに対して同時に実行もできます.

いかがでしたでしょうか?

私はややこしい環境構築とかなしでダウンロードしてサクっと使えてしまったことに驚きました.まだFuncUnitはβ版なので現状で実務に堪えうるかどうかは要検証ですが,まずは好感触です.

HTML5の登場によりWEBアプリにおけるJavaScriptの占める比重がどんどん大きくなっていくかと思います.非同期通信・サーバプッシュ型イベント・ローカルストレージの利用・ローカルデバイスの利用・自由に描画できる画面・マルチスレッド処理(?)などなどが入り混じったアプリケーションを,Javaよりよっぽど自由な言語で作成し,それのテストをブラウザからポチポチとやるのは悪夢以外のなにものでもありません.

ガンガン使い倒して,JavaScriptが今より複雑になってしまう前に,ブラウザポチポチからおさらばしておきましょう.