Rails 6.1 + Active Storage + factory_bot で undefined method `file_fixture_path' エラーが発生した
Active Storage で画像のアップロードを実装しているサービスで、factory_bot で画像付きのテストデータを生成するために以下のような factory を定義していた。
include ActionDispatch::TestProcess
factory :foo_image do
image { fixture_file_upload("spec/fixtures/foo.jpg") }
end
このサービスのアプリケーションを Rails 6.0.x から Rails 6.1.x (Rails 6.1.3.1) にアップデートしたところ、 以下のエラーが発生するようになった。
Failure/Error: image { fixture_file_upload("spec/fixtures/foo.jpg") }
NoMethodError:
undefined method `file_fixture_path' for #<Class:0x00007ff5e40a8278>
Did you mean? fixture_file_upload
対応方法
そもそも fixture_file_upload
を提供する ActionDispatch::TestProcess
を include
せずに Rack::Test::UploadedFile
を直接呼び出す方式に書き換えた。
factory :foo_image do
image { Rack::Test::UploadedFile.new("spec/fixtures/foo.jpg", "image/jpg") }
end
fixture_file_upload
を使わなくなった理由は、ActionDispatch::TestProcess
を include
すると、
session
メソッドや cookies
メソッドといった、テストデータの生成に関係のないメソッドも include
されることが分かったから。
fixture_file_upload
がやっていることは渡されたパスを良い感じに解決して Rack::Test::UploadedFile
を呼んでいるだけなので、簡単に置き換えることができる。
この件については 2018 年頃に factory_bot の Issue で言及されていた。
なので、余計なメソッドが include
されるのは Rails 6.1.x だけではなく、以前のバージョンからあった。
余談: ActiveSupport::Testing::FileFixtures
を include
する方法を採用しなかった理由
今回のエラーはそもそも fixture_file_upload
から参照する file_fixture_path
が見つからないというのが原因。
インターネットを検索すると ActiveSupport::Testing::FileFixtures
を include
する方法がヒットする。
file_fixture_path
というクラス変数が生成されるのでエラーが発生しなくなるという話。
これはこれで良いと思うが、2 つの module を include
することにモヤモヤ感があり、後でハマりそうな気がしたので採用しなかった。
敢えてこれを採用するメリットを考えてみたが、file_fixture_path
をカスタマイズすることで、ファイルパスを短く書ける、とかだろうか。
# rails_helper.rb
FactoryBot::SyntaxRunner.class_eval do
include ActionDispatch::TestProcess
include ActiveSupport::Testing::FileFixtures
self.file_fixture_path = "spec/fixtures"
end
# foo_images.rb
factory :foo_image do
image { fixture_file_upload("foo.jpg") }
end