fixtures の変更
Fixtureも変わっています。大きくは、関連オブジェクトをidではなくhuman readableな名前で記述できるようになった点ですが、このエントリは関係ありません。隠れた変更点?として、fixtureのキャッシュ機能が追加されています。
Rails 2.0 のFixtureクラスでは、Fixtureをファイルから読み込んでインスタンス化するcreate_fixtureメソッドに、
table_names_to_fetch = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) } unless table_names_to_fetch.empty? ... end
のような形で、キャッシュされている(すでにインスタンス化されている)Fixtureは、ファイルの再読込をしないようなロジックが追加されています。つまり、Rails 1.2.Xでは、test/unit のテストケースがN個ある場合に、test/fixtures はN回読み込まれるわけですが、Rails 2.0.X では、これが1回で済むようになりました、ということです。
このキャッシュ単位は、fixturesのファイル単位、つまりモデルごと、もう少しいえばテーブルごと、ということになります。
で、WebJourneyでは、(1)「初期データとして投入するfixture」と(2)「テストデータとして投入するfixture」を2つのFixtureファイルにしておいて、テスト実行時には、Fixtureクラスのlaod_fixtureメソッドを上書きして、2つのファイルを読み込めるように書き換えていたのですが、このキャッシュ機能の性で、2つめのファイルが読み込めなくなってしまいました。
(1)も(2)も、同じモデルのFixtureなので、(1)のファイルを読み込み終わったときに、「キャッシュしたぜ」フラグが立って、(2)のFixtureを作ろうとしたときに、「キャッシュにあるぜ」といわれて、読み込まれない、というオチ。
実際には、Rails 1.2.X ではこんな感じの記述を test/test_helper.rb に書いていました。db/init には (1)の初期データ用fixtureをいれておいて、test/fixturesには(2)のテスト用fixtureをいれておくようにしています。
class_inheritable_accessor :initialize_fixtures_path self.initialize_fixtures_path = File.join(RAILS_ROOT, "db/init") alias :load_fixtures_from_test_directory :load_fixtures def load_fixtures @initialize_fixtures = {} @exist_fixtures = [] fixture_table_names.each do |table_name| @exist_fixtures << table_name if File.exists?(File.join(initialize_fixtures_path, table_name + ".yml")) end fixtures = Fixtures.create_fixtures(initialize_fixtures_path, @exist_fixtures, fixture_class_names) unless fixtures.nil? if fixtures.instance_of?(Fixtures) @initialize_fixtures[fixtures.table_name] = fixtures else fixtures.each do |f| @initialize_fixtures[f.table_name] = f end end end load_fixtures_from_test_directory # load fixtures defined in db/init directory @initialize_fixtures.each { |t, f| f.insert_fixtures } end
Rails オリジナルのfixture(test/fixtures)をロードした時点(load_fixtures_from_test_directory)で、DBには、test/fixturesで定義されているデータのみが存在することになるので(それまでにあったデータはトランザクションコンテキストの中で一度消える、テストが終わるとRollbackされるのでもどる:use_transactional...=trueの場合)、ここで、db/initにあるデータをinsertしにいきます。
ところが、Rails 2.0 では、この load_fixtures_from_test_directory を実行しても、それ以前にFixture.create_fixtureをしているので、「キャッシュにあるぜ」フラグのせいで、正しくtest/fixtures の内容がDBに登録されません。
というわけで、「キャッシュにあるぜ」フラグを明示的に解消すべく、
Fixtures.reset_cache fixtures = Fixtures.create_fixtures(initialize_fixtures_path, @exist_fixtures, fixture_class_names) ...(snip)... Fixtures.reset_cache load_fixtures_from_test_directory
として回避します。要するに、Fixtureのキャッシュなんかいらないよ、テストなんて時間がかかったっていいじゃないか、というモチベーション。「果報は寝て待て」ということです。
Fixtureは1テーブルにつき1ファイル、というConventionを無視した報い、ですね。。