Rails 5.1以降のシステムテストをRSpecで実行する(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

@jnchitoさんの以下の記事も合わせてどうぞ。

Rails 5.1以降のシステムテストをRSpecで実行する(翻訳)

RSpec 3.7登場前のfeature spec は、実物(またはヘッドレス)のブラウザ環境でJavaScriptの絡むアプリのやり取りをフルスタックでテストする手段でした。最近リリースされたRSpec 3.7では、Railsのsystem testを元にしたsystem specが追加されました。Rails 5.1ではActionDispatch::SystemTestCaseが導入され、実際のブラウザでのテストに使えるようになりました。設定済みのCapybaraラッパーが提供され、Railsフレームワークに組み込まれている多くの機能を利用できるようになりました。設定済みのCapybaraのおかげで、従来は正しく設定するためにトリッキーになりがちだった手動設定の手間が大きく軽減されました。feature specの代わりにsystem specを使うメリットは次のとおりです。

  1. テストが終わるとデータベースの変更が自動でロールバックされるので、database_cleaner gemを用いてロールバックを手動で設定する必要がない。
  2. driven_byを使うと、specごとにブラウザを簡単に切り替えられる。
  3. テストが失敗すると即座にスクリーンショットを自動撮影し、ターミナルにもスクリーンショットをインライン表示する。この機能は事前設定済みなので、capybara-screenshot gemなどが不要になる。

上述のメリットに加えて、RSpecチームがRails 5.1以降ではfeature specよりもsystem specを推奨している点も強調しておきたいと思います。

RSpecのsystem specをヘッドレスChrome向けにセットアップする

まずはRailsプロジェクトを新規作成しましょう。いつものように--skip-testオプションを追加して、RailsデフォルトのminitestではなくRSpecを使うようにします。

rails --version
Rails 5.2.0.beta2
rails new rspec-system-specs --skip-test --skip-active-storage

セットアップでGemfilerspec-railsを追加する作業以外で最も面倒なのは、ヘッドレスChromeブラウザのテストに必要なgemを見極めることでしょう。必要なgemのリストを以下に示します。

group :development, :test do
  # Capybara system testingとselenium driverのサポートを追加
  gem 'capybara', '~> 2.16.1'
  gem 'selenium-webdriver', '~> 3.8.0'
  # Chromeでのシステムテスト実行に使うchromedriverを簡易インストール
  gem 'chromedriver-helper', '~> 1.1.0'
  gem 'rspec-rails', '~> 3.7.2'
end

chromedriverがインストールされていることを確認します(訳注: 以下はMacでhomebrewを使う場合です)。

brew install chromedriver
chromedriver --version
#> ChromeDriver 2.34.522932 (4140ab217e1ca1bec0c4b4d1b148f3361eb3a03e)

spec/test_helper.rbspec/rails_helper.rbを生成します。

rails g rspec:install

system specを書く

ここでは単純なhome#indexアクションがroutes.rbのrootとして設定されていて、app/views/home/index.html.erbで以下をレンダリングするとします。

<h1 id="title">Hello World</h1>

最初のsystem specを次のようにspec/system/home_spec.rbに実装できます。

require 'rails_helper'

describe 'Homepage' do
  before do
    driven_by :selenium_chrome_headless
  end

  it 'shows greeting' do
    visit root_url
    expect(page).to have_content 'Hello World'
  end
end

ここではヘッドレスChromeを使いたいので、:selenium_chrome_headlessドライバを設定しています。Capybaraでこの他に提供されている登録済みドライバは、:rack_test:selenium:selenium_chromeです。ブラウザの解像度などの高度な設定オプションについてのドキュメントは、driven_byをご覧ください。

ちゃんと動くかどうか確認します。

$ rspec
Puma starting in single mode...
* Version 3.11.0 (ruby 2.5.0-p0), codename: Love Song
* Min threads: 0, max threads: 4
* Environment: test
* Listening on tcp://127.0.0.1:50713
Use Ctrl-C to stop
.

Finished in 1.41 seconds (files took 2.04 seconds to load)
1 example, 0 failures

ドライバをspecごとに設定したくない場合は、spec_helper.rbでデフォルトのグローバル設定を以下のように行なえます。

RSpec.configure do |config|
  config.before(:each, type: :system) do
    driven_by :selenium_chrome_headless
  end
end

後は必要に応じて個別のspecファイルでデフォルトのドライバ設定を上書きします。

テスト失敗時のブラウザスクリーンショット

system specで特筆したい点は、specが失敗したときにブラウザのスクリーンショットを自動で撮影し、ターミナルにインライン出力してくれる便利な機能があることです。

spec失敗時

この機能はRailsのシステムテストに組み込まれているので、feature specのようにcapybara-screenshotなどによるサポートを手動で設定する必要がありません。

JavaScriptのテスト

ここまではサーバー側でのコンテンツレンダリングのテストだけなので、今度はクライアント側のJavaScriptを追加して、JavaScriptが動くブラウザ(ここではヘッドレスChrome)がsystem specで使えることを示してみましょう。

$ ->
  $('#title').text('Hello Universe')

specのアサーションをexpect(page).to have_content 'Hello Universe'に変更すると、クライアント側でのJavaScript変更のspecテストはこれまで同様パスします。

$ rspec
Finished in 1.99 seconds (files took 2.44 seconds to load)
1 example, 0 failures

データベースの自動ロールバック

上述したように、system specでのデータベース変更は自動的にロールバックされます。ページにデータベース出力を少し追加してテストしてみましょう。

<h1 id="title">Hello World</h1>
<p>
  Planet count: <%= Planet.count %>
</p>

specを変更してレコードをseedし、出力されたレコードのcountのアサーションを行うspec exampleを新たに追加します。

describe 'Homepage' do
  before do
    driven_by :selenium_chrome_headless
    puts 'creating a planet'
    Planet.create!(name: 'Mars')
  end

  it 'shows greeting' do
    visit root_url
    expect(page).to have_content 'Hello Universe'
  end

  it 'shows planet count' do
    visit root_url
    expect(page).to have_content 'Planet count: 1'
  end
end

rspecの結果は次のようになります。

$ rspec --format d
creating a planet
  shows greeting
# spec example追加後のデータベース自動ロールバック
creating a planet
  shows planet count

Finished in 1.52 seconds (files took 1.03 seconds to load)
2 examples, 0 failures

できました!feature specではspec example実行後にデータベースの状態をクリーンアップするためにdatabase_cleaner gemの設定が必要でしたが、これで不要になりました。

本記事のデモに用いた例のソースコードはGitHubに置いてあります。

関連記事

TestProf: Ruby/Railsの遅いテストを診断するgem(翻訳)

ソフトウェアテストでstubを使うコストを考える(翻訳)

Rails: ActiveRecord関連付けのpreload/eager-loadをテストする2つの方法(翻訳)

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の半分ほど、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れてそれぞれ一部を翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好き。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ