LoginSignup
15
8

More than 3 years have passed since last update.

Rails6 のちょい足しな新機能を試す73(driven_by編)

Posted at

はじめに

(多分)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 でした。悪しからず :bow:)

$ 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 を利用するように変更します。

docker-compose.yml
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 を取得するようにします。

app/controllers/home_controller.rb
class HomeController < ApplicationController
  def index
    @user_agent = request.user_agent
  end
end

View を変更する

View で User Agent の情報を表示するように変更します。

app/views/home/index.html.erb
<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 を修正します。

spec/support/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 のラッパーになっています。`

lib/rspec/rails/example/system_example_group.rb
# 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つのパターンだけを想定した書き方にしました。

spec/system/home_spec.rb
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

参考情報

15
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
8