Write and Run

it's a simple way, but the only way.

JavaScriptでテストを怠惰に書く

突然ですが

私立・プログラミングキャンプ 2012 東京大会 - #upcamp : ATND に行ってきました。意味不明だと思いますけど、ずっと電磁リレーで4ビット加算器作ってました。

ちなみに時間がなくて回路がバグってます

今回はそのとき用いた怠惰なテストの書き方についてです。

テストを書くというのはダルいもんで、まぁとにかくダルいもんで、昔書いたテストとかなんだか意味わからんことになってたり、個人的にはユースケース的なテストがあるとサンプルの代わりにもなって便利だと思ってるんですけど、テストフレームワークとか使うとそういう雰囲気もなくなっちゃって(そりゃ当然だが)あまり嬉しくないので……(ry

という愚痴はさておき、手軽に書けて嬉しいのは自明です。

どこに書くか

わざわざ test ディレクトリ作って test/hoge.js に書いて mocha で実行……もいいですが、やっぱめんどくさい。なんかさっと書きたくなったときに実装の近くに書いてしまいたい。

以下は実際ハッカソンで書いた relay-sim のコードの一部。電気の実装。

// require 省略
function Electricity () {
  this.VCC = new Point(voltage.POSITIVE);
  this.GND = new Point(voltage.NEGATIVE);
}

Electricity.prototype.spread = function (point) {
  var dests = point.getConnections().filter(function (point) {
    return point.voltage === voltage.NEUTRAL;
  });
  
  var result = dests.map(function (destPoint) {
    destPoint.voltage = point.voltage;
    return this.spread(destPoint);
  }.bind(this)).reduce(function (prev, curr) {
    return prev.concat(curr);
  }, []).concat(point);

  return result;
};

Electricity.prototype.run = function () {
  var vccFootprints = this.spread(this.VCC),
      gndFootprints = this.spread(this.GND);

  return vccFootprints.concat(gndFootprints);
};

module.exports = Electricity;

// ここから下がテストコード
if (!module.parent) {
  var expect = require('expect.js');

  var el1 = new Electricity(),
      led1 = new Led();

  el1.VCC.connect(led1.A);
  el1.GND.connect(led1.C);
  var footprints = el1.run();

  expect(led1.status).equal(states.OFF);

  led1.drive();
  expect(led1.status).equal(states.ON);

  console.log('finished!');
}

コメントにあるように "if (!module.parent)" 以下がテストです。expect.js を使って簡単に書いてあります。ちなみにこのテストでは LED に電気を流して光ることを確かめています。

"!module.parent" の意味

Node.js な人ならわかると思いますが、単純に言うと直接実行された時のみ実行ということです。なので、モジュールとして外から require された場合は実行されませんが、"node hoge.js" と直接実行するとテストが走ります。便利ですね。

どんなとき使うか

正直、まじめに大規模なプロダクトで使うのは如何なものかなぁ、というこの書き方ですが、今回のようにハッカソンで一気に作ってしまいたいとき、でも変なバグと戦いたくないしテスト書いたほうがいいよなーって時、とても便利だと思います。