RSpecでファイルのダウンロードをテストする方法
【結論】
・リンクをクリックしてCSVファイルをダウンロードする機能の場合、response_headers
でレスポンスヘッダの情報を見てテストが出来る
・response_headers
はrack_test
では利用できるが、他のドライバ(headless chromeなど)ではresponse_headers
が使えない場合がある
・その場合はダウンロードしたファイルを直接確認する必要がある(コードは本文参照)
【目次】
【本題】
レスポンスヘッダの情報を見る方法
send_file
でファイルダウンロードを行う機能をテストする場合、簡単な方法の一つはresponse_headers
でレスポンスヘッダの情報を見る方法です。
例えば、リンクをクリックするとCSVファイルがダウンロードされる機能をテストする場合、click
後に以下の様に実装します。
expect(page.response_headers['Content-Disposition']).to include('xxx.csv')
ダウンロードファイルを直接確認する方法
先ほどの方法は、ドライバにrack_test
を利用している場合は有効ですが、それ以外のドライバでは利用できない場合があります。
例えば、JavaScriptの操作も含まれるので、ドライバにheadless chrome
を指定した場合などは、レスポンスヘッダを見るAPIが用意されていない為、先ほどの方法が使えません。
その様な場合には、ダウンロードされたファイルを直接確認する必要があります。
1:DownloadHelperを用意する
まず、ダウンロードするファイルの保存場所の定義や、ファイルの有無を確認する為のメソッドを定義したDownloadHelper
を作成します。
以下が、そのコードです。
module DownloadHelper TIMEOUT = 10 PATH = Rails.root.join("tmp/downloads") extend self def downloads Dir[PATH.join("*")] end def download downloads.first end def download_content wait_for_download File.read(download) end def wait_for_download Timeout.timeout(TIMEOUT) do sleep 0.1 until downloaded? end end def downloaded? !downloading? && downloads.any? end def downloading? downloads.grep(/\.crdownload$/).any? end def clear_downloads FileUtils.rm_f(downloads) end end
downloads.rb
など分かりやすいファイル名を付けて、spec/support
配下に格納します。
なお、supportディレクトリ配下にファイルを置く場合は、rails_helper.rb
の以下のコードのコメントアウトを外す必要があります。
コメントアウトのままだと読み込まれません。
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
2:rails_helperにDownloadHelperの読み込み
DownloadHelper
が作成できたら、その読み込みや、ファイル保存先の指定などをrails_helper.rb
に記述します。
RSpec.configure do |config| # ... config.include DownloadHelper, type: :system, js: true config.before(:suite) { Dir.mkdir(DownloadHelper::PATH) unless Dir.exist?(DownloadHelper::PATH) } config.after(:example, type: :system, js: true) { clear_downloads } # Chrome mode config.before(:each, type: :system, js: true) do driven_by :selenium, using: :headless_chrome, screen_size: [1920, 1080] page.driver.browser.download_path = DownloadHelper::PATH end end
上から順に以下の様なことを定義しています。
JavaScript操作が必要なシステムテストが実行される際、
DownloadHelper
の読み込みテストが実行される一番はじめに、
tmp/downloads
ディレクトリの有無を確認+作成単一のテスト
example
が完了後、ダウンロードしたファイルの削除(初期化)JavaScript操作が必要なシステムテストが実行される際、ドライバに
headless_chrome
を指定JavaScript操作が必要なシステムテストが実行される際、ダウンロードファイルの保存先を指定
3:テストコードをダウンロードされたファイルを確認する内容に修正する
最後に、該当のテストに、読み込んだファイルをチェックするテストコードを記述します。
expect(download_content).to include('XXX')
なお、ファイルの中身ではなく、ファイル名でテストを実行したい場合は、DownloadHelper
に以下の様にファイル名を取得するメソッドを定義して、それをテストで呼び出せば良いです。
def download_file_name wait_for_download File.basename(download) end
expect(download_file_name).to include('XXX.csv')
参考情報
poltergeistからheadless chromeへ移行する時に気をつけること - メドピア開発者ブログ
Testing File Downloads with Capybara and ChromeDriver | Collective Idea
capybaraでファイルダウンロードをテストする - Qiita