テストfixtureの中にerbを書いて動的に生成する
テストをちゃんとやろうと試みた事は何度かあった。
でもfixture書くのがダルくてテストを書くまで辿りつけず挫折。
DBのデータからfixtureを作る方法もあるけど、それだとデータが大量すぎて扱いきれない。
でもやっぱりテストはやっておいた方が安心だよなぁと思う今日この頃。
安心というか自分のコードに自信が持てなくなってきたので要所要所はテストしておきたい。
しかしfixtureを書くのがだるい。
そもそもfixtureってDRYじゃない気がする。
そこでふと思いだしたのは「database.yml って DRY じゃないよね。」という話。
あたりで紹介されてます。
Railsのyamlはerbを通してからyamlとして扱われるらしいのでfixtureにもerbが書ける*1はず。
やってみよう。
普通のfixtureの気にいらないところ
こういうfixtureがあったとする。
# == Schema Information # Schema version: 1 # # Table name: estimates # # id :integer not null, primary key # code :string(32) not null # quotation_id :integer not null # issued_at :datetime # issued_staff_id :integer # created_at :datetime not null # created_staff_id :integer not null # updated_at :datetime not null # updated_staff_id :integer not null # disable :boolean not null # --- not_issued: id: 1 code: EST-0001 created_at: 2009-02-02 22:22:22 created_staff_id: 1 updated_at: 2009-02-02 22:22:22 updated_staff_id: 1 disable: false issued: id: 2 code: EST-0002 issued_at: 2009-02-03 23:23:23 issued_staff_id: 1 created_at: 2009-02-02 22:22:22 created_staff_id: 1 updated_at: 2009-02-02 22:22:22 updated_staff_id: 1 disable: false
気にいらないところ。
- xxx_at系の日時を書くの面倒
- DRYじゃない
1. 日時を動的に生成しちゃおう!!!
という事で、こんな風にしてみた。
not_issued: id: 1 code: EST-0001 created_at: <%= Time.now %> created_staff_id: 1 updated_at: <%= Time.now %> updated_staff_id: 1 disable: false
意図するものになっているか確認する方法
てっとりばやく確認するには以下の方法が使えます。
$ ruby -r erb -r yaml -e 'YAML.load(ERB.new(ARGF.read).result).to_yaml.display' test/fixtures/estimates.yml --- not_issued: updated_at: Fri Feb 13 15:27:59 +0900 2009 updated_staff_id: 1 code: EST-0001 id: 1 disable: false created_at: Fri Feb 13 15:27:59 +0900 2009 created_staff_id: 1 :
ステキ
2. DRYに挑戦!!!(YAMLでがんがる)
単純なyamlだったらyamlだけでも満足。
だってyamlにはマージ機能があるんですもの。
--- common_fixture: &common_fixture created_at: <%= Time.now %> created_staff_id: 1 updated_at: <%= Time.now %> updated_staff_id: 1 disable: false not_issued: id: 1 code: EST-0001 <<: *common_fixture issued: id: 2 code: EST-0002 <<: *common_fixture issued_at: 2009-02-03 23:23:23 issued_staff_id: 1
とか。いい感じです。
でもfixtureだとこれではダメ。
なぜかというと'YAML.load(ERB.new(ARGF.read).result).to_yaml.display'すると
--- common_fixture: updated_staff_id: 1 updated_at: Fri Feb 13 16:02:07 +0900 2009 disable: false created_staff_id: 1 created_at: Fri Feb 13 16:02:07 +0900 2009 issued: updated_at: Fri Feb 13 16:02:07 +0900 2009 updated_staff_id: 1 code: EST-0002 issued_staff_id: 1 id: 2 issued_at: 2009-02-03 23:23:23 disable: false created_at: Fri Feb 13 16:02:07 +0900 2009 created_staff_id: 1 not_issued: updated_at: Fri Feb 13 16:02:07 +0900 2009 updated_staff_id: 1 code: EST-0001 id: 1 disable: false created_at: Fri Feb 13 16:02:07 +0900 2009 created_staff_id: 1
テンプレートとして使っているcommon_fixture が残ってしまいます。
しかも、common_fixtureには必須レコードがないので実際にfixtureとして使おうとするとエラーになりまする。
2. DRYに挑戦!!!(ERBの力を借りる)
と、いう事でYAMLのマージは使えません。
しかしERBの力を借りればなんでもできる!!!
まぁそんなに難しい事はなくて変数に文字列を入れておいて後から展開すればいいだけの話ではないかと。
こんな感じ
<% common_fixture = <<EOD created_at: #{Time.now} created_staff_id: 1 updated_at: #{Time.now} updated_staff_id: 1 disable: false EOD %> --- not_issued: id: 1 code: EST-0001 <%= common_fixture %> issued: id: 2 code: EST-0002 <%= common_fixture %> issued_at: 2009-02-03 23:23:23 issued_staff_id: 1
評価してみましょう。
--- issued: updated_staff_id: 1 updated_at: Fri Feb 13 18:05:27 +0900 2009 code: EST-0002 issued_staff_id: 1 id: 2 issued_at: 2009-02-03 23:23:23 disable: false created_staff_id: 1 created_at: Fri Feb 13 18:05:27 +0900 2009 not_issued: updated_staff_id: 1 updated_at: Fri Feb 13 18:05:27 +0900 2009 code: EST-0001 id: 1 disable: false created_staff_id: 1 created_at: Fri Feb 13 18:05:27 +0900 2009
ほらー。ほらほらー。
か・ん・ぺ・き。
さらなる進化
変数じゃなくて関数にしてみてもおもしろいかもしれませんね。
<% def common_fixture(id) return <<"EOD" id: #{id} code: EST-#{"%04d" % id} created_at: #{Time.now} created_staff_id: 1 updated_at: #{Time.now} updated_staff_id: 1 disable: false EOD end %> --- not_issued: <%= common_fixture 1 %> issued: <%= common_fixture 2 %> issued_at: <%= Time.now %> issued_staff_id: 1
これを評価すると。。。
--- issued: updated_staff_id: 1 updated_at: Fri Feb 13 18:09:45 +0900 2009 code: EST-0002 issued_staff_id: 1 id: 2 issued_at: Fri Feb 13 18:09:45 +0900 2009 disable: false created_staff_id: 1 created_at: Fri Feb 13 18:09:45 +0900 2009 not_issued: updated_staff_id: 1 updated_at: Fri Feb 13 18:09:45 +0900 2009 code: EST-0001 id: 1 disable: false created_staff_id: 1 created_at: Fri Feb 13 18:09:45 +0900 2009
ステキー!!!!
変数のままでステキな事を
ここまで書いてちょっと思った。
この程度ならわざわざ def common_fixture しなくても変数でできるんじゃね?
<% common_fixture = <<EOD id: %1$d code: EST-%1$04d created_at: #{Time.now} created_staff_id: 1 updated_at: #{Time.now} updated_staff_id: 1 disable: false EOD %> --- not_issued: <%= common_fixture % 1 %> issued: <%= common_fixture % 2 %> issued_at: <%= Time.now %> issued_staff_id: 1
これでどう?
評価すると。。。
--- issued: updated_staff_id: 1 updated_at: Fri Feb 13 18:15:34 +0900 2009 code: EST-0002 issued_staff_id: 1 id: 2 issued_at: Fri Feb 13 18:15:34 +0900 2009 disable: false created_staff_id: 1 created_at: Fri Feb 13 18:15:34 +0900 2009 not_issued: updated_staff_id: 1 updated_at: Fri Feb 13 18:15:34 +0900 2009 code: EST-0001 id: 1 disable: false created_staff_id: 1 created_at: Fri Feb 13 18:15:34 +0900 2009
感無量ッス
いやーこれでfixture作るのが楽しくなってtestが書け(ry
*1:この表現あってる?