はじめに
(多分)Rails 6 に追加された新機能を試す第73段。 今回は、 driven_by
編です。
Rails 6 では、 System Test で使われる driven_by
メソッドがブロックを受け付けることにより、ブラウザ特有の設定ができるようになりました。
Ruby 2.6.3, Rails 6.0.0.rc2, RSpec(rspec-rails) 3.8.2 で確認しました。
なお、今回は、ブラウザ実行の環境として、 selenoid/chrome を利用しました。
ChromeDriver と Google Chrome のバージョンは以下の通りです。
(Rails 6.0.0 がリリースされましたが、動作確認当時の最新版は Rails 6.0.0.rc2 でした。悪しからず )
$ chromedriver --version
ChromeDriver 76.0.3809.68 (420c9498db8ce8fcd190a954d51297672c1515d5-refs/branch-heads/3809@{#864})
$ google-chrome --version
Google Chrome 76.0.3809.87
Rails 6.0.0.rc2 は gem install rails -v 6.0.0rc2 --prerelease
でインストールできます。
$ rails --version
Rails 6.0.0.rc2
今回は、 Docker で RSpec の System Spec を実行するための設定メモ をベースに話を進めます。
ブラウザの User Agent の情報を画面に表示する機能をテストしてみたいと思います。
Google Chrome には、デバイス名を変更することで、 User Agent の情報を変更する機能があるので、それを使ってテストを書いてみたいと思います。
なお、テストは、RSpec を使います。
docker-compose.yml を変更する
Docker で RSpec の System Spec を実行するための設定メモ では、 selenium/standalone-chrome-debug を使ったのですが、今回の目的を満足するように、うまく動作してくれませんでした。
selenoid/chrome を利用するように変更します。
version: '3'
services:
web:
...
environment:
...
- "SELENIUM_DRIVER_URL=http://chromedriver:4444"
chromedriver:
image: selenoid/chrome
ports:
- "4444:4444"
....
Controller と View を作る
Controller と View を作ります。
$ bin/rails g controller home index
Controller を変更する
request
から user_agent
を取得するようにします。
class HomeController < ApplicationController
def index
@user_agent = request.user_agent
end
end
View を変更する
View で User Agent の情報を表示するように変更します。
<h1>Home#index</h1>
<p>
<%= @user_agent %>
</p>
driven_by を使って Chrome で User Agent を切り替える
当初、 RSpec の example の metadata で切り替えようとしたのですが、うまくいかなかった(example 単位で、デバイス名を切り替えられなかった) ので、環境変数 DEVICE_NAME
を使って、変更できるようにします。
Docker で RSpec の System Spec を実行するための設定メモ で使った capybara.rb
を修正します。
require 'capybara/rspec'
RSpec.configure do |config|
config.before(:each, type: :system) do |example|
driven_by :selenium, using: :headless_chrome, screen_size: [1024, 768], options: {
browser: :remote,
url: ENV.fetch('SELENIUM_DRIVER_URL'),
desired_capabilities: :chrome
} do |driver_option|
# driver_option.add_emulation(device_name: example.metadata[:device_name])
driver_option.add_emulation(device_name: ENV.fetch('DEVICE_NAME', nil))
driver_option.add_argument('no-sandbox')
end
Capybara.server_host = 'web'
Capybara.app_host = 'http://web'
end
end
ここで、 driven_by
メソッドを呼び出すときにブロックを指定して、 device_name
の指定と Chrome の引数として no-sandbox
を指定していることに注意してください。これが、今回のちょい足し機能です。
なお、この driven_by
メソッドは、rspec-rails に含まれるメソッドです。
rspec-rails の driven_by
メソッドは、Rails 6 の ActionDispatch::SystemTestCase.driven_by
のラッパーになっています。`
# rspec-rails (lib/rspec/rails/example/system_example_group.rb)より抜粋
...
def driven_by(*args, &blk)
@driver = ::ActionDispatch::SystemTestCase.driven_by(*args, &blk).tap(&:use)
end
...
system spec を書く
system spec を書いてみます。
User Agent の一部が index ページに表示されるかどうかテストします。
今回は、意図的に iPhone と HeadlessChrome の2つのパターンだけを想定した書き方にしました。
require 'rails_helper'
RSpec.describe type: :system do
# using :device_name metadata does not work fine.
# describe 'when iPhone 8', device_name: 'iPhone 5' do
# it do
# expect(page).to have_content 'iPhone'
# end
# end
describe 'index' do
before do
visit home_index_path
end
let(:user_agent_pattern) do
device_name = ENV.fetch('DEVICE_NAME', nil)
case device_name
when /iphone/i
'iPhone'
else
'HeadlessChrome'
end
end
it 'show user agent' do
expect(page).to have_content user_agent_pattern
end
end
end
spec を実行する
spec を実行してみます。
$ bundle exec rspec
...
Finished in 1.39 seconds (files took 2 seconds to load)
1 example, 0 failures
環境変数で iPhone 7
を指定してみます。
$ env DEVICE_NAME='iPhone 7' bundle exec rspec'
...
Finished in 1.38 seconds (files took 2.01 seconds to load)
1 example, 0 failures
今度は、 Galaxy S5
を指定してみます。
実際に表示されている User Agent の情報には、 HeadlessChrome は含まれず、 User Agent が異なっている(Android が含まれている)ことがわかりますね。
$ env DEVICE_NAME='Galaxy S5' bundle exec rspec
...
Failures:
1) {:type=>:system} index show user agent
Failure/Error: expect(page).to have_content user_agent_pattern
expected to find text "HeadlessChrome" in "Home#index\nMozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3329.0 Mobile Safari/537.36"
# ./spec/system/home_spec.rb:30:in `block (3 levels) in <top (required)>'
Finished in 3.64 seconds (files took 2.03 seconds to load)
1 example, 1 failure'`
問題点
直接、今回の機能を試すこととは関係ない(と思われる)話ですが...。
spec が失敗したときにブラウザのスクリーンショットが取られるのですが、スクリーンショットは白い画面になるだけで、何も取れていませんでした。原因がわかっていません。
試したソース
試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try073_add_browser_capabilities