はじめに
2017年4月13日に、Headlessモード付きのChromeがまもなく登場するというアナウンスがありました。
下記ドキュメントによるとバージョン59からHeadlessモードが導入されるとのことです。
参考: Headless mode - Chrome Platform Status
Headless(ヘッドレス)モードとは、GUIを起動しない(つまり目に見える形でブラウザを起動しない)モードのことです。
これによりブラウザを自動実行させる際の速度が向上したり、GUIを持たないサーバー上でブラウザを実行できたりするようになります。
また、Rails 5.1では新しくSystemTestCaseというテストケースが導入されました。
SystemTestCaseはエンドツーエンド(E2E)テスト、すなわちブラウザ上の動きを検証するためのテストケースです。
これを使えばJavaScriptを利用する画面のテストケースも書けるようになります。
参考: Rails 5.1のSystemTestCaseを試してみた - Qiita
SystemTestCaseはデフォルトでChromeを起動するようになっています。
ですが、Headlessモードではないため、ローカルマシンで実行するとChromeが自動的に起動して画面に表示されます。
そこでこの記事ではSystemTestCaseでHeadlessモードのChromeを使う方法を紹介します。
おことわり
本記事の執筆時点(2017年4月19日)ではRails 5.1も、Chrome 59もまだ公式にリリースされていません。
公式リリース後にはこの記事の内容が使えなくなるかもしれないのでご注意ください。
2017.7.26追記:正式リリース版でも試してみました
正式リリース版が出揃ったので、以下の環境(Mac環境)で試してみました。
- Rails 5.1.2
- Chrome 59.0.3071.86
- ChromeDriver 2.31
- Capybara 2.14.4
- selenium-webdriver 3.4.4
上記のバージョンを使うと、後述する「ウインドウサイズの変更をスキップする(一時的な回避策)」が不要になっていました!
動作確認したい方はGitHubの以下のブランチを動かしてみてください。
デモ
Headlessモードを使うと、こんな感じでテストを実行した際にChromeが画面に表示されなくなります。
Rails 5.1のSystemTestCaseでHeadlessモードのChromeを使ってみた。(ちょっとわかりにくいけど、Chromeが起動しても画面に表示されないところがポイントです) pic.twitter.com/suoXbtaCeP
— Junichi Ito (伊藤淳一) (@jnchito) April 18, 2017
それでは以下が手順です。
手順
ふつうにSystemTestCaseを使うテストコードを書く
最初にHeadlessモードではないChromeを使ってテストコードを書きます。
本記事では以下の記事で作成したテストコードをベースにして説明を進めます。
Rails 5.1のSystemTestCaseを試してみた - Qiita
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
end
require "application_system_test_case"
class UsersTest < ApplicationSystemTestCase
setup do
# テストデータの作成
@user = User.create!(name: 'いとう')
end
test "yubinbango.js" do
# User編集画面を開く
visit edit_user_path(@user)
# Nameに"いとう"が入力されていることを検証する
assert has_field?('Name', with: 'いとう')
# 郵便番号を入力
fill_in 'Postal code', with: '158-0083'
# 住所が自動入力されたことを検証する
assert has_field?('Address', with: '東京都世田谷区奥沢')
# 更新実行
click_button 'Update User'
# 正しく更新されていること(=画面の表示が正しいこと)を検証する
assert_text 'User was successfully updated.'
assert_text 'いとう'
assert_text '158-0083'
assert_text '東京都世田谷区奥沢'
end
end
Chrome 59をインストールする
Headlessモード付きのChromeはバージョン59以降で導入されますが、本記事の執筆時点ではまだ公式にリリースしていません。
そこで以下のURLから開発中バージョンのChromeを入手し、インストールします。
インストール後、Chromeのバージョンが59以上になっていることを確認します。
最新版のChromeDriverをインストールする
下記のリンクから最新版のChromeDriverを入手し、インストールします。
本記事の執筆時点ではバージョン2.29が最新バージョンです。
ただし、Chrome 58までしかサポートしていないため、一部使えない機能が出てきます。(後述)
ChromeDriverのインストール方法は以下の記事を参考にしてください。
Rails 5.1のSystemTestCaseを試してみた - Qiita
ChromeDriverをインストールしたら、念のためバージョンを確認してください。
$ chromedriver -v
ChromeDriver 2.29.461585 (0be2cd95f834e9ee7c46bcc7cf405b483f5ae83b)
Railsやテスト関連のgemをアップデートする
この記事では以下のバージョンのgemで動作確認しています。
- rails 5.1.0.rc1
- capybara 2.13.0
- selenium-webdriver 3.3.0
バージョンが古い場合はGemfileを更新してbundle update
してください。
gem 'rails', '~> 5.1.0.rc1'
group :development, :test do
gem 'capybara', '~> 2.13.0'
gem 'selenium-webdriver', '~> 3.3.0'
end
HeadlessモードでChromeを起動するようにオプションを付ける
環境のアップデートが終わったので、いよいよSystemTestCaseをHeadlessモードで動かすようにオプションを付けます。
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
- driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
+ caps = Selenium::WebDriver::Remote::Capabilities.chrome("chromeOptions" => {"args" => %w(--headless)})
+ driven_by :selenium, using: :chrome, screen_size: [1400, 1400], options: { desired_capabilities: caps }
end
それではテストを実行してみましょう。
$ rails test:system
Run options: --seed 22839
# Running:
E
Error:
UsersTest#test_yubinbango.js:
Selenium::WebDriver::Error::UnknownError: unknown error: cannot get automation extension
from unknown error: page could not be found: chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/_generated_background_page.html
(Session info: headless chrome=59.0.3071.9)
(Driver info: chromedriver=2.29.461585 (0be2cd95f834e9ee7c46bcc7cf405b483f5ae83b),platform=Mac OS X 10.12.4 x86_64)
test/system/users_test.rb:11:in `block in <class:UsersTest>'
Error:
UsersTest#test_yubinbango.js:
Selenium::WebDriver::Error::UnknownError: unknown error: cannot get automation extension
from unknown error: page could not be found: chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/_generated_background_page.html
(Session info: headless chrome=59.0.3071.9)
(Driver info: chromedriver=2.29.461585 (0be2cd95f834e9ee7c46bcc7cf405b483f5ae83b),platform=Mac OS X 10.12.4 x86_64)
bin/rails test test/system/users_test.rb:9
Finished in 21.419511s, 0.0467 runs/s, 0.0000 assertions/s.
1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
おっと、エラーが出てしまいましたね。
そこで次のステップでこのエラーを回避します。
ウインドウサイズの変更をスキップする(一時的な回避策)
このエラーの原因を調べたところ、どうやらテスト実行中にブラウザのウインドウサイズを変更しようとするところでエラーが発生しているようでした。
これはまだChromeDriverがChrome 59を公式サポートしていないのが原因かもしれません。
というわけで、この問題が解決されるまで以下のようなモンキーパッチを当てて、一時的にウインドウサイズのリサイズ処理をスキップすることにします。
begin
require 'action_dispatch/system_testing/driver'
module ActionDispatch
module SystemTesting
class Driver
private
# Monkey patch for ChromeDriver 2.29
def register
Capybara.register_driver @name do |app|
Capybara::Selenium::Driver.new(app, { browser: @browser }.merge(@options)).tap do |driver|
# driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
end
end
end
end
end
end
rescue LoadError
# ignore
end
こうしてやるとテストがパスするはずです。
$ rails test:system
Run options: --seed 58423
# Running:
Puma starting in single mode...
* Version 3.7.1 (ruby 2.4.0-p0), codename: Snowy Sagebrush
* Min threads: 0, max threads: 1
* Environment: test
* Listening on tcp://0.0.0.0:55250
Use Ctrl-C to stop
.
Finished in 1.782806s, 0.5609 runs/s, 3.3655 assertions/s.
1 runs, 6 assertions, 0 failures, 0 errors, 0 skips
ChromeDriverがChrome 59を公式サポートするようになれば、このモンキーパッチは不要になるはずなので削除するようにしてください。
SystemTestCaseでHeadlessモードのChromeを使用する手順は以上です!
参考:diffを見る
HeadlessモードのChromeで動かすために加えた変更点は下記のリンクにあるdiffで確認できます。
備考
シンプルなスクリプトでHeadlessモードの動作を確認する
Rails環境なしで手っ取り早くChromeをHeadlessモードで動かしてみたい場合は、以下のようなコードで動作確認することができます。
require 'selenium-webdriver'
caps = Selenium::WebDriver::Remote::Capabilities.chrome("chromeOptions" => {"args" => %w(--headless)})
driver = Selenium::WebDriver.for :chrome, desired_capabilities: caps
driver.navigate.to "http://google.com"
element = driver.find_element(:name, 'q')
element.send_keys "Hello WebDriver!"
element.submit
puts driver.title
driver.quit
【謝辞】上記のコードは下記ブログの内容を参考にさせてもらいました。
rubyからchrome操作してみた by selenium-webdriver - shoprevのブログ
PhantomJSからHeadless Chromeへの移行が急速に進むかも?
これまでRailsのテスト(RSpecのフィーチャスペック等)ではHeadlessブラウザとしてPhantomJS + Poltergeistがよく使われていました。
しかし、Headlessモード付きのChromeが登場したことを受けて、PhantomJSのメンテナが「PhantomJSはもうメンテナンスしない」とアナウンスしました。
[Announcement] Stepping down as maintainer - Google グループ
なので、これからはPhantomJSからHeadless Chromeへの移行が急速に進む可能性が高いかもしれません。
まだ試していないこと
以下のようなユースケースではまだHeadless Chromeを試していません。
- CI環境(CircleCI、Wercker、Travis等)でHeadlessモードのChromeを実行する
- RSpecのフィーチャスペックでHeadlessモードのChromeを実行する
実際に試した人がいたらコメント等で教えてもらえると助かります
(2017.6.15追記)
RSpecで使う場合はこちらの記事が参考になりそうです。
(2017.7.26追記)
CI環境で動かしたり、RSpecと組み合わせて動かしたりするバージョンも作ってみました。
- CI対応版(Travis、CircleCI)
-
RSpec版
-
rails_helper.rb
を参照。
-
こちらも参考にしてみてください。
まとめ
というわけでこの記事ではRails 5.1のSystemTestCaseでHeadlessモードのChromeを使う方法を紹介しました。
HeadlessモードのChromeが気になっているRailsプログラマのみなさんは、よかったら参考にしてみてください。