javascriptでテスト駆動開発 Mocha+expect.js+testem【後編】
前回はtestemを使って自動でテストが走る環境作りをしました。
今回も引き続きTDDっぽいことしていきます。
addメソッドをテストする
前回作ったcalcモジュールのaddメソッドをテストしていきます。
まずは新しいターミナルを立ちあげて、
プロジェクトのディレクトリに移動してtestemを起動します。
$ cd ~/path/to/my/project $ testem
これでtestemが立ち上がりました。
では次に、前回作ったテストファイルにテストケースを追記してみます。
// test/calc.test.js var expect = require('expect.js'); var calc = require('../lib/calc.js'); describe('add', function(){ it('1と1を与えたら2を返す', function(){ var result = calc.add(1, 1); expect(result).to.be(2); }); // テストケースを追記 it('1と2を与えたら4にならない', function(){ var result = calc.add(1, 2); expect(result).not.to.be(4); }); });
testemを実行しているので自動でテストが走り、
ターミナルには
✔ 2 tests complete.
と表示されました。
さらにテストケースを追記してみます。
// 追記 it('1と"2"を与えたら3を返す', function(){ var result = calc.add(1, "2"); expect(result).to.be(3); });
testemには何やらエラーのようなものが表示されました。
add 1と"2"を与えたら3を返す ✘ Error: expected '12' to equal 3 at Assertion.assert (/path/to/myapp/node_modules/expect.js/expect.js:99:13) at Assertion.be.Assertion.equal (/path/to/myapp/node_modules/expect.js/expect.j s:200:10) at Assertion.(anonymous function) [as be] (/path/to/myapp/node_modules/expect.j s/expect.js:73:24) at Context.<anonymous> (/path/to/myapp/test/calc.test.js:15:22) at Test.Runnable.run (/path/to/username/.nave/installed/0.10.5/lib/node_modules/mocha/lib/runnabl e.js:213:32)
1行目にテストケースの内容、2行目に実際の結果が表示されてます。
テストケースでは結果として3が返ってくることを期待しましたが、
実際のところは文字列の"12"が返ってきました。
これはJavaScriptの仕様で、+演算子は文字列としての結合を優先するためです。
これはバグになるもとなので、今回作るaddメソッドでは
1+"2"を3と返すように修正します。
// lib/calc.js // 数値型に変換する関数作ってみた var castToNumber = function(v){ return typeof v === 'string' ? Number(v) : v; }; exports.add = function(a, b){ return castToNumber(a) + castToNumber(b); };
testemの画面には、
✔ 3 tests complete.
はい、テストが通りました!
・・ですが、まだ穴があります。
もうすこしテストケースを作成します。
// テストケース追記 it('1と{}を与えたら1を返す', function(){ var result = calc.add(1, {}); expect(result).to.be(1); });
testemは、
add 1と{}を与えたら1を返す ✘ Error: expected '1[object Object]' to equal 1
へー、こんな結果になるのか。
ということでコードを修正します。
// 文字列は数値型に変換して、他は0を返すように変更してみた var castToNumber = function(v){ if (typeof v === 'number') return v; if (typeof v === 'string') { return Number(v); } else { return 0; } }; exports.add = function(a, b){ return castToNumber(a) + castToNumber(b); };
testemの結果は、
✔ 4 tests complete.
全てのテストケースをパスしてグリーンになりました。
テスト駆動っぽいことをしてみて
常にテストが走る環境で開発することでコードに自信を持てるし、
バグが生じてもすぐに発見できるようになります。
テストっていうと軽いニュアンスになってしまいますが、
Railsとかだとspec(仕様)にテストコードを書くので、
テストコードを書くということイコール仕様をコードで表現するってことです。
テストコードを書くときは、
- describe('〇〇', function(){});
- 〇〇について説明します。
- it('〜〜', function(){});
- それは、〜〜です。
と、仕様を明確にしながら書いていきます。
たとえば、addメソッドは引数を2つとりますが、
引数が1つしか渡されなかった時のふるまいはどうなるでしょう?
テストケースにはまだこの記述がありません。
つまり、試してみないとわからない状態です。
引数が1つしか渡されなかったとき、
実際にはbがundefinedになるので0に変換されますが、
NaNを返すか、Exceptionを返すという選択肢もあります。
他の開発者がこの疑問を思ったとき、まっさきにテストケースに目を通せば
addメソッドのふるまいを理解できることでしょう。
おわりに
前回、今回とテスト回でした。
実際の業務ではガッチリとテスト駆動開発できていないので、
いざやってみるといいですね。
やはりtestemが良い感じです。
次回はフロント側を勉強するためBackbone.jsを扱う予定です。