メドピア開発者ブログ

集合知により医療を再発明しようと邁進しているヘルステックリーディングカンパニーのエンジニアブログです。PHPからRubyへ絶賛移行中!継続的にアウトプットを出し続けられるようにみんなでがんばりまっす!

最近のheadless chromeを利用したファイルダウンロードのテスト方法について

こんにちは。メドピアのRuby(Rails)化をお手伝いしている@willnetです。最近大阪Ruby会議02に妻子を連れて参加したのですが、👶が行き帰りの新幹線に合わせて寝てくれたおかげで大変スムーズに移動できました。

さて、以前poltergeistからheadless chromeへ移行する時に気をつけることというブログエントリを書きました。

その中で、ファイルダウンロードのテストをheadless chromeで実行するための設定について書いています。しかし、この設定では最近のchrome(chromedriver)では動かなくなってしましました。このエントリでは最新のやり方について紹介します。

これまでの設定例

以前のブログエントリに掲載したコードを一部再掲します。

Capybara.register_driver :headless_chrome do |app|
  driver = Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
      login_prefs: { browser: 'ALL' },
      chrome_options: {
        args: %w(headless disable-gpu window-size=1900,1200 lang=ja no-sandbox disable-dev-shm-usage),
      }
    )
  )
  bridge = driver.browser.send(:bridge) # ここからがファイルダウンロード用の設定
  path = "session/#{bridge.session_id}/chromium/send_command"
  bridge.http.call(
    :post, path,
    cmd: 'Page.setDownloadBehavior',
    params: {
      behavior: 'allow',
      downloadPath: DownloadHelper::PATH.to_s,
    }
  )
  driver
end

Capybara.javascript_driver = :headless_chrome

これまで、headless chromeでのファイルダウンロード機能はデフォルトで無効だったので、有効にするために上記のようなコードを書く必要がありました。

しかし最近のchromedriver(v77以降)の仕様変更により、上記のコードは動かなくなってしまいます。

新しいchromedriverでは、上記のような設定をせずともデフォルトでファイルのダウンロードが有効になっています。このとき、デフォルトではカレントディレクトリがダウンロード先になります。上記の設定がこのデフォルトの挙動に置き換わってしまうため、DownloadHelper::PATHにファイルがダウンロードされることを期待しているすべてのダウンロード関連のテストが失敗するようになります。

(追記)この現象はv77時点ではlinux版のchromedriverでのみ起こるようです。macの場合はこれまでの書き方か、後述しているSelenium::WebDriver::Chrome::Driver#download_path=を利用した書き方でのみテストが通り、次の解決策で紹介した書き方だとテストが失敗します(ややこしいですね…)。

解決策

次のように修正すると、ダウンロード先をDownloadHelper::PATHで設定したディレクトリに変更できます。

Capybara.register_driver :headless_chrome do |app|
  browser_options = Selenium::WebDriver::Chrome::Options.new
  browser_options.args << '--headless'
  browser_options.args << '--disable-gpu'
  browser_options.args << '--no-sandbox'
  browser_options.args << '--disable-dev-shm-usage'
  browser_options.args << '--lang=ja'
  browser_options.args << '--window-size=1920,1200'
  # この行がメインの変更
  browser_options.add_preference(:download, default_directory: DownloadHelper::PATH.to_s)
  Capybara::Selenium::Driver.new(
    app, browser: :chrome, options: browser_options
  )
end

Capybara.javascript_driver = :headless_chrome

以前の設定と比べて、selenium-webdriverに対するオプションの渡し方が新しいものに変わっています。が、そこは本筋ではないので置いておいて、browser_options.add_preference(:download, default_directory: DownloadHelper::PATH.to_s)がメインの変更点です。これによりchromedriverでのダウンロードディレクトリの設定を変更することができます。

ちなみにselenium-webdriverには3.13.0以降でSelenium::WebDriver::Chrome::Driver#download_path= メソッドが生えているため、v77未満のchromeを利用している場合は、bridge = driver.browser.send(:bridge) ...のようにせずとも次のように書くことができるようになっています(内部でやっていることは一緒です)。こちらのほうが簡潔で良い感じですね。

# 略
  driver = Capybara::Selenium::Driver.new(
    app, browser: :chrome, options: browser_options
  )
  driver.browser.download_path = DownloadHelper::PATH.to_s
  driver
end
# 略

お手持ちのchromeのバージョンに合わせてご利用ください。

謝辞

このエントリで紹介した内容について、@jnchitoさんに情報提供いただきました。ありがとうございました(\( ⁰⊖⁰)/)


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html