mofmof.js の Unit Test(ver 2) - もっと短く簡単に書けるよ
mofmof.js にはユニットテスト機能があり、同期/非同期テストの混在や遅延評価もシンプルに記述できます。
以下のコードは、http://mofmof-js.googlecode.com/svn/trunk/test/base.js.htm でテストできます。
(ε・◇・)з mofmof.js には Unit Test 機能が最初から付いてるよ
(ε・◇・)з でもね、テスト用のコードは沢山書きたくないよ。リアルに疲れるよ
(ε・◇・)з じゃあ、短く書けるようにしなきゃね!
というわけで、
よりお手軽にするため、先日作成した String#test と Array#test の機能を大幅に強化しました。
おさらい
mofmof.js では、(↓)のように Unit Test を記述できます。
"関数で遅延評価 > 配列の左辺と右辺を評価 > 真偽値で評価".test({ arg: { msg: "hello" }, "関数で遅延評価": function() { return this.arg.msg === "hello"; // -> true }, "配列の左辺と右辺を評価": ["aaa", "aaa"], // -> true "真偽値で評価": 123 === 123 });
実行結果です。
┌ 関数で遅延評価 > 配列の...() │ 関数で遅延評価 > 配列の...[ 関数で遅延評価: true ] │ 関数で遅延評価 > 配列の...[ 配列の左辺と右辺を評価: true = Type_isLike( "aaa", "aaa" ) ] │ 関数で遅延評価 > 配列の...[ 真偽値で評価: true ] └ 関数で遅延評価 > 配列の...( span: 00.010 )
(ε・◇・)з ロジック量に対してコード量が多いよね。つまりノイズが多いよね
(ε・◇・)з コマンド文字列長いし、テスト項目名を2回も書かなきゃだし…
別名(alias)を指定
テスト項目名に別名(alias)を指定できます。
"長い長い名前:短名" のように
テスト項目名 + : + 別名
でテスト項目名を指定します。
このように指定すると、"長い長い名前" と "短名" の両方がテスト項目名として有効になります。
"長い長い名前:短名 > a-long-long-cat:cat > high-order:hide".test({ arg: { msg: "hello" }, "短名": function() { return this.arg.msg === "hello"; // -> true }, cat: ["aaa", "aaa"], // -> true // 二重に指定した場合は "high-order" 側が実行されます "high-order": 123 === 123, // -> true hide: 123 === 345 // -> この項目は実行されません }, "別名(alias)を指定可能にしました");
実行結果です。
┌ 別名(alias)を指定可能にしました() │ 別名(alias)を指定可能にしました[ 短名: true ] │ 別名(alias)を指定可能にしました[ cat: true = Type_isLike("aaa", "aaa") ] │ 別名(alias)を指定可能にしました[ high-order: true ] └ 別名(alias)を指定可能にしました( span: 00.004 )
具体的なタイトルを指定
コマンド文字列の代わりに、テストの具体的なタイトルを指定できます。
タイトルを指定しない場合はコマンド文字列がタイトルになります。
"fn1".test({ fn1: "123" == 123 // -> true }, "タイトルを指定可能にしました"); // <- タイトル
実行結果です。
┌ タイトルを指定可能にしました() │ タイトルを指定可能にしました[ fn1: true ] └ タイトルを指定可能にしました( span: 00.005 )
比較関数を指定
テストを配列で指定した場合に、配列の3番目に比較関数を指定できます。
比較関数を省略すると、Type.isLike で類似検索と深度探索を行います。
mm.has は 集合A に集合B(または要素)が含まれる場合に true になります。
"cat > dog".test({ cat: ["cat", ["meerkat", "neko", "cat"], mm.has], // -> true dog: true, }, "比較関数を指定可能にしました");
実行結果です。
┌ 比較関数を指定可能にしました() │ 比較関数を指定可能にしました[ cat: true = mm_has( "cat", ["meerkat","neko","cat"] ) ] │ 比較関数を指定可能にしました[ dog: true ] └ 比較関数を指定可能にしました( span: 00.714 )
関数内部から参照する引数を指定
String#test([], title, arg) のように第三引数を指定すると、テスト関数内部から this.arg で参照できます。
var title = "ref arg"; var arg = [0,1,NaN,Infinity,"a","",null,void 0,document,/a/]; "".test([ mm.has([0], arg), // -> true function() { return mm.has(0, this.arg); // -> true } ], title, arg); // <- ここです
実行結果です。
┌ ref arg() │ ref arg[ 0: true ] │ ref arg[ 1: true ] └ ref arg( span: 00.008 )
テストの成功と失敗をハンドリング
String#test.callback を定義しておくと、
テスト成功/失敗の都度 String#test.callback をコールバックします。
callback(result) の形式でコールバックします。result は以下の値をもつ Hash です。
result.ok | Boolean | 成功でtrue, 失敗でfalse |
result.name | String | テスト項目名 |
result.msg | String | 詳細情報 |
result.pass | Number | 累積成功数 |
result.miss | Number | 累積失敗数 |
result.items | Number | テスト項目数 |
String.prototype.test.callback = function(result) { // result = { ok, name, msg, pass, miss, items } if (result.ok) { mm.log("PASS", mm.dump(result, true)); } else { mm.log.error("MISS", mm.dump(result, true)); } }; // 2つ成功し、1つ失敗するテストケース "".test([ function(callback) { 2..lazy(this, function() { callback(false); }); }, "JavaScript".anagram("VBScript"), 123 === 123 ]);
実行結果です。
┌ () │ [ 0: false ] MISS {"items":3,"miss":1,"msg":"","name":"0","ok":false,"pass":0} │ [ 1: false ] MISS {"items":3,"miss":2,"msg":"","name":"1","ok":false,"pass":0} │ [ 2: true ] PASS {"items":3,"miss":2,"msg":"","name":"2","ok":true,"pass":1} └ ( span: 02.004 )
コマンド文字列を省略
配列を指定するとコマンド文字列を省略可能です。
順番に実行します。
"".test([ mm.has(0, [0, 1, 2]), // -> true mm.has([0, 1], [0, 1, 2]), // -> true mm.has([0, 1, "a"], [0, 1, 2]) === false // -> true ]);
実行結果です。
┌ () │ [ 0: true ] │ [ 1: true ] └ ( span: 00.007 )
タイトルが無いと、何のテストかわかりませんね。タイトルを指定しましょう。
"".test([ mm.has(0, [0, 1, 2]), // -> true mm.has([0, 1], [0, 1, 2]), // -> true mm.has([0, 1, "a"], [0, 1, 2]) === false // -> true ], "mm.has のテスト"); // <- title
実行結果です。
┌ mm.has のテスト - mm.has() │ mm.has のテスト - mm.has[ 0: true ] │ mm.has のテスト - mm.has[ 1: true ] │ mm.has のテスト - mm.has[ 2: true ] └ mm.has のテスト - mm.has( span: 00.09 )
改造前, 改造後
昨日までは(↓)のように沢山タイプしなければいけなかったテストコードが…
"関数で遅延評価 > 配列の左辺と右辺を評価 > 真偽値で評価".test({ arg: { msg: "hello" }, "関数で遅延評価": function() { return this.arg.msg === "hello"; // -> true }, "配列の左辺と右辺を評価": ["aaa", "aaa"], // -> true "真偽値で評価": 123 === 123 });
String#test のブラッシュアップにより、ここまで短く書けるようになりました。
"".test([ function() { return this.arg.msg === "hello"; }, ["aaa", "aaa"], 123 === 123 ], "", { msg: "hello" });
実行結果です。
┌ () │ [ 0: true ] │ [ 1: true = Type_isLike( "aaa", "aaa" ) ] │ [ 2: true ] └ ( span: 00.09 )
Array#test も上記と同じ結果になります。
[ function() { return this.arg.msg === "hello"; }, ["aaa", "aaa"], 123 === 123 ].test("", { msg: "hello" });
(ε・◇・)з 余計なコードが減ってスッキリ ノイズすくない( S/N比高い )
(ε・◇・)з テストマシマシ。コードヘラシヘラシ!
このエントリをより良く理解するには、過去のエントリ
- Future と Stream という同期/非同期処理の混在をシンプルにコード化できる車輪を再発明したよ - latest log
- mofmof.js のUnit Test機能 - latest log
をご覧ください。