単一化でテストすれば良いじゃない
最近、こんなテストをいくつか書いていて、イラっとしました。
post(:create_note, { :note => { ... } }) assert_equal( { "id" => 13, "created_at" => Time.now, "updated_at" => Time.now, .... }, JSON.parse(@response.body))
このテストにはいくつか問題があります。
- idは13だと思っていて大丈夫か?
- postした時刻とassert_equalする時刻は等しいか?
- created_atとupdated_atが等しいことはテストできるか?
どれも保証されません。*1
さて、これは、なんかどこかで見たことがあります。
まず連想するのはMLやHaskellのパターンマッチです。(ここにOCamlの例を書こうと思ったけど、忘れていたので省略。)パターンマッチには制限があって、3番目の制約「created_atとupdated_atが等しい」は記述することができません(一つのパターンの中に同じ変数を複数回書くことはできません)。ちょっと弱い。
で、ツリーオートマトンじゃないかとか、正規表現で行けるんじゃないかとか、いろいろ考えたんですけど、かなりぴったりのものを見つけました。
単一化で良いんじゃないか。
こんな感じで書きましょう。
post(:create_note, { :note => { ... } }) assert_unify( { "id" => :"'a", "created_at" => :"'b", "updated_at" => :"'b", .... }, JSON.parse(@response.body))
ここではメタ変数として、'a、'bを導入します。'aと'bは、それぞれなんでも良いんだけど、'bに対応するものは、それぞれの出現で等しくないといけません。*2
この例だと、idはなんでも良いんだけどなんかidというフィールドが無いことは許されません。created_atとupdated_atもなんでも良いんだけど、この二つは等しくないといけません。
こんな感じ。