valid,invalid

関心を持てる事柄について

PhantomJS + Poltergeist を Selenium + Headless Chrome で置き換える (1) Rails + Capybara による feature spec 編

レガシーに半身浴しているような、ふだんなかなか触らないけれど現役のレポジトリに潜んでいた亡霊を退治!!! Poltergeist + PhantomJS を消し去り、 Selenium + Headless Chromium に置き換えた。

このレポジトリは Rails による API バックエンドと SPA フロントエンド両方を管理しているもので、以下の2箇所で PhantomJS に依存していた。

  1. Capybara による feature spec。PhantomJS の driver として poltergeist を使用。
  2. Teaspoon による frontend の JavaScript test。Teaspoon は JS のテストランナーでもあり PhantomJS driver も内包している 。

両方について書いたら思いの外長くなってしまったのでこの記事では 1. の方のリプレイスについてのみ書く。後半の 2. の方は PhantomJS + Poltergeist を Selenium + Headless Chrome で置き換える (2) Teaspoon による JavaScript test 編 - valid,invalid で。

動機

  1. PhantomJS は元々ベースにしている Webkit が古くて最新のブラウザで利用可能な JS, CSS を充分サポートできていなかったのに加え、Chrome 59 の headless mode リリース以降コアメンテナが離れたことでプロジェクトの将来性が危ぶまれる。

  2. PhantomJS が依存している Qt のインストールでしょっちゅう問題が起きる。

  3. 他のレポジトリは既に Selenium + headless Chrome を使うようにアップデート済。

結果、可及的速やかにリプレイスすることが望ましい状況だと思いました。(というか、全部リプレイス済だとなぜか勝手に思ってた)

変更内容について

このリプレイスは同僚が既に他のレポジトリで実践していた内容をほとんど拝借した。とはいえ自分で pull request を送るのだからきちんと理解したいと思い Selenium webdriver の Ruby binding 実装を少し読んでみたりもした。

実際のコード差分

Gemfile は少しスッキリ。開発環境では Docker compose を使い、アプリケーションとは別の Docker コンテナ上で立ち上げている selenium/standalone-chrome に接続しているので今回は chromedriver や chromedriver-helper gem のインストールはしていない。

# Gemfile
group :test do
  gem 'capybara'
-  gem 'capybara-webkit'
-  gem 'phantomjs', :require => 'phantomjs/poltergeist'
-  gem 'poltergeist'
+  gem 'selenium-webdriver'
end

capybara.rb の設定方法はぐぐると諸々出てくるので今なら悩みどころは多くなさそうだ。

Headless Capybara Feature Specs with Chrome RSpec の feature spec でヘッドレス Chrome を使う - Speee DEVELOPER BLOG

など。

# spec/support/capybara.rb
- require 'capybara/poltergeist'
- require 'phantomjs'
+ require 'capybara/rspec'
+ require 'selenium-webdriver'

- Capybara.javascript_driver = :poltergeist
+ Capybara.javascript_driver = :selenium

- Capybara.register_driver(:poltergeist) do |app|
+ Capybara.register_driver :selenium do |app|

-  options = { } # 省略

+  options = {
+    browser: :chrome,
+    desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
+      chrome_options: {
+        args: %w(headless window-size=1680,1050), 
+      }
+    )
+  }

+  options.merge!(url: ENV['SELENIUM_URL']) if ENV['SELENIUM_URL'].present?

-  Capybara::Poltergeist::Driver.new(app, options)
+  Capybara::Selenium::Driver.new(app, options)

  # その他 remote で Selenium を動かすための設定が続くがリプレイスとあまり関係ないので省略
end

実際はアプリケーション固有の設定などもあってもう少し複雑になったがだいたいこのような感じ。

Selenium の URL を環境変数で渡せることで local でも remote でも実行できるようになっている(はず)。

ちなみにサンプルによく出てくる disable-gpu フラグは Google Developers ガイドにも載っているので広まったのだと思うが、現時点で最新の Chrome 65 では不要になっている。Chrome 63で動いたという報告も見かけた。(ただし Windows では動くかわからない)

737678 - Headless: make --disable-gpu flag unnecessary - chromium - Monorail


Selenium なんとなく設定周りが複雑でとっつきづらい印象があったのだが wiki は丁寧に書かれている上にコードも思ったほど入り組んでいなかった。

どうやら自分の敬遠は Selenium をラップしてる各々のライブラリの統一感の無さとかから来ているのかもしれない…。


PhantomJS + Poltergeist を Selenium + Headless Chrome で置き換える (2) Teaspoon による JavaScript test 編 - valid,invalidに続く