Google が開発した JavaScript 用テストフレームワーク Google JS Test を使う
JavaScript のテストを書きたくなって調べていたら、2011年9月29日に公開されたばかりの Google JS Test を見つけた。
これは Google 社内で使われている JavaScript のテスト環境らしく、同じく同社が開発している V8 エンジンの上で動作する。
DOM 操作はできないのだけれど、後述するように、Google は DOM へのアクセスを隔離することでテストが可能になるし、コードが簡潔になる、という理由から特に問題視はしていないようだ。
インストールも簡単だったし、JS のテストを書きたい人は使ってみるといいかも知れない。
チュートリアル
英語が読むのが面倒な人はリンク先の最初のコードを some_functions.js、2個目のコードを namespace.js、3個目のコードを some_functions_test.js という名前で保存して
gjstest --js_files=namespace.js,some_functions.js,some_functions_test.js
を実行する。この例から分かるように、js_files オプションで指定された順番に JavaScript のソースコードが読み込まれる。
最後に指定したsome_functions_test.js の中に記述されたテストの実行結果がターミナルに表示されるはず。
エラー文を見れば、12行目のテストで [ 'Hello', 'world' ] が期待されているところに [ 'Hello', 'World' ] が返されていることとか、64行目のテストで TypeError で始まり must be a number で終わる文字列がエラーとして投げられることを期待されているところでエラーが発生していないことが分かる。
結局、僕はこんな感じにデバッグした。
// Return some interesting words. myproject.getSomeWords = function() { return ['Hello', 'world']; }; // Add the supplied numbers and then call back with the result. myproject.addNumbersAndCallBack = function(a, b, resultCallback) { if (typeof a === 'string') { throw 'TypeError first argument must be a number'; } resultCallback(a + b); };
HTML出力
テスト結果を HTML 形式で出力することもできる
gjstest --js_files=namespace.js,some_functions.js,some_functions_test.js --html_output_file=test_result.html
こうすることで test_result.html というファイルが出力される。
ブラウザで見るとこんな感じで表示される。
サーバで定期実行させて、その結果をブラウザから見れるのは便利そう。
テスト用関数
チュートリアル中で使っているのは、expectThat や expectEq や expectCall。
他にも使いそうなのはだいたい揃っているし、自分で新しい関数を定義することもできる。
モック
callback 関数が正しい引数で呼び出されているかどうかをテストするには、上のチュートリアル中でもやっているように createMockFunction で作ったモックオブジェクトと、expectCall を組み合わせて使う。
DOM操作をテストする
JavaScript のテストをするのに障壁になるのが、DOM や setTimeout や Ajax が絡むような場合。
Google JS Test のドキュメントには、DOM 操作のテストをしたい場合、window や document を関数から追い出せば、前述のモックを使ってテストを行うことができる、と主張している。
例で示すと、エラー文を表示する以下のような関数があるとする。
function writeErrorMessage(msg, elementName) { // Write out the error message. document.getElementsByName(elementName).innerText = 'Error: ' + msg; }
writeErrorMessage 関数内で DOM 操作を行なっている。この例に対し、実際に elementName のテキストに変化したかどうかをテストするには、テスト環境で DOM をエミュレートしなければならないが、そんな機能は Google JS Test にはない。
でも、この関数を次のように変形させれば、テストをすることができる。
function writeErrorMessage(msg, element) { // Write out the error message. element.innerText = 'Error: ' + msg; }
今までは
writeErrorMessage('msg', 'hoge');
のように実行していたのが
var elem = document.getElementsByName('hoge'); writeErrorMessage('msg', elem);
のようにインタフェースが変更されてるので、関数内で elem に対して innerText プロパティが変更されているかどうかを、例えばこんな感じでテストすることができる。
function WriteErrorMessageTest() {} registerTestSuite(WriteErrorMessageTest); WriteErrorMessageTest.prototype.InnerText = function () { var msg = 'msg', elem = {}; writeErrorMessage(msg, elem); expectEq(elem.innerText, 'Error: ' + msg); };
Google の主張によれば、こうする方が関数もシンプルになるし、テストもシンプルになるからいいのだと。
setTimeout のテストも同様のテクニックを使うことで記述可能らしい。