give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

【動画付き】Everyday RailsのサンプルアプリをRails 6で動かす際に必要なテストコードの変更点

はじめに

前々回のブログでは、2019年10月時点における「Everyday Rails - RSpecによるRailsテスト入門」(以下、Everyday Rails)のサンプルアプリケーションの環境セットアップ手順を紹介しました。

blog.jnito.com

ただし、この手順に従ってもサンプルアプリの実行環境はRails 5.1.1、rspec-rails 3.8.0にとどまります。 2019年10月時点での最新環境はRails 6.0.0、rspec-rails 3.9.0です。

そこで、このエントリでは Everyday Railsのサンプルアプリを最新のRails環境にアップデートした際に発生する、テストコードの変更点を紹介します。

【もくじ】

対象となる実行環境について

あらためて実行環境の情報をまとめておきます。

変更前の環境 変更後の環境 *
Rails 5.1.1 6.0.0
rspec-rails 3.8.0 4.0.0.beta2
(正式版は3.9.0)
Ruby 2.4.9 2.6.5
その他のgem - できる限り最新化

* 2019年10月時点の最新環境

この記事では上の表のように、GitHubのこのコミット(8b3800f)を2019年10月時点の最新の環境にアップグレードすることを想定します。

また、ここには細かく書きませんが、サンプルアプリケーションが依存する各種ライブラリ(gem)もRails 6で動作するよう、最新版にアップデートします。

実行環境を最新化する手順について

Everyday Railsのサンプルアプリケーションを最新化するためには、順を追って作業を進めていく必要があります。 ここでは以前僕がQiitaに書いた以下の記事に従ってアップグレードを進めるものとします。

qiita.com

また、アップグレードに伴う一連の作業は動画としてもアップしているので、こちらも参考にしてみてください。 (前編、後編、各30分)

【前編】永久保存版!?伊藤さん式・Railsアプリのアップグレード手順

【後編】永久保存版!?伊藤さん式・Railsアプリのアップグレード手順

Rails 6環境で発生するテストコード関連の変更点

実行環境をRails 6にアップグレードすると(そしてRailsだけでなく依存するgemを一通り最新版にアップデートすると)、Everyday Railsのサンプルアプリケーションのテストコードは以下のような変更作業が必要になります。

chromedriver-helperをwebdriversに変更する

chromedriver-helper gemは開発が終了してしまったので、webdrivers gemに変更します。

 # Gemfile
-gem 'chromedriver-helper'
+gem 'webdrivers'

chromedriver-helper gemを使い続けていると、gemをアップデート(bundle update)した際に以下のメッセージが表示されます。

Post-install message from chromedriver-helper:

  +--------------------------------------------------------------------+
  |                                                                    |
  |  NOTICE: chromedriver-helper is deprecated after 2019-03-31.       |
  |                                                                    |
  |  Please update to use the 'webdrivers' gem instead.                |
  |  See https://github.com/flavorjones/chromedriver-helper/issues/83  |
  |                                                                    |
  +--------------------------------------------------------------------+

参考 サポートが終了したchromedriver-helperからwebdrivers gemに移行する手順 - Qiita

また、この変更にあわせて、spec/support/vcr.rbignore_hosts の設定を追加します。

 # spec/support/vcr.rb

 config.ignore_localhost = true
+config.ignore_hosts 'chromedriver.storage.googleapis.com'
 config.configure_rspec_metadata!

この設定がないと、テスト実行時に以下のようなエラーが発生します。

VCR::Errors::UnhandledHTTPRequestError:


  ================================================================================
  An HTTP request has been made that VCR does not know how to handle:
    GET https://chromedriver.storage.googleapis.com/LATEST_RELEASE_77.0.3865
  
  (中略)
  ================================================================================
# ./spec/system/tasks_spec.rb:3:in `<main>'
# -e:1:in `<main>'

shoulda-matchersのブランチ指定を解除する

Everyday Railsのサンプルコードが作成された当時は shoulda-matchers gemは rails-5 ブランチを指定していましたが、現在はブランチを指定せずに最新版をインストールすれば大丈夫です。(バージョン4.1.0以上であればRails 6もサポート済み)

 # Gemfile
-gem 'shoulda-matchers',
-  git: 'https://github.com/thoughtbot/shoulda-matchers.git',
-  branch: 'rails-5'
+gem 'shoulda-matchers'

factory_botの記法を変更する

factory_bot 4.11からファクトリの記法が次のように変わっています。

# 4.10以前
factory :user do
  first_name "Aaron"
end

# 4.11以降(ブロックの { } が必要)
factory :user do
  first_name { "Aaron" }
end

記法が古いと、テスト実行時に以下のようなエラーが発生します。

/Users/jnito/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/factory_bot-5.1.1/lib/factory_bot/definition_proxy.rb:99:in `method_missing': undefined method 'first_name' in 'user' factory
Did you mean? 'first_name { "Aaron" }'
 (NoMethodError)
    from /Users/jnito/dev/sandbox/everydayrails-rspec-2017/spec/factories/users.rb:3:in `block (2 levels) in <main>'
    from /Users/jnito/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/factory_bot-5.1.1/lib/factory_bot/syntax/default.rb:18:in `instance_eval'
    (以下略)

ただし、既存のファクトリをいちいち手で書き換える必要はありません。以下のコマンドを実行すれば自動的に書き換えてくれます。

# rubocop-rspec gemをインストール
$ gem install rubocop-rspec

# 自動修正コマンドを実行
$ rubocop --require rubocop-rspec --only FactoryBot/AttributeDefinedStatically --auto-correct

参考 【翻訳】factory_bot 4.11で非推奨になった静的属性(static attributes) - Qiita

rspec-railsは4.0.0.beta2をインストールする

2019年10月時点ではrspec-railsがまだ完全にRails 6に対応していません。正式対応が完了するまではbeta版を使用します。

 # Gemfile
-gem 'rspec-rails', '~> 3.8.0'
+gem 'rspec-rails', '~> 4.0.0.beta2'

なお、Rails 6とrspec-rails 3.9.0を組み合わせると、以下のようなエラーや警告が発生します(実際に発生するエラーから一部抜粋)。

DEPRECATION WARNING: formats is deprecated and will be removed from Rails 6.1 (called from block (3 levels) in <main> at /Users/jnito/dev/sandbox/everydayrails-rspec-2017/spec/controllers/home_controller_spec.rb:6)
DEPRECATION WARNING: ActionView::Template#initialize requires a locals parameter (called from block (3 levels) in <main> at /Users/jnito/dev/sandbox/everydayrails-rspec-2017/spec/controllers/home_controller_spec.rb:6)

ActionView::Template::Error: wrong number of arguments (given 2, expected 1)

  0) HomeController returns a 200 response
     Failure/Error: get :index

     ActionView::Template::Error:
       wrong number of arguments (given 2, expected 1)
     # ./spec/controllers/home_controller_spec.rb:12:in `block (2 levels) in <main>'
     # -e:1:in `<main>'
     # ------------------
     # --- Caused by: ---
     # ArgumentError:
     #   wrong number of arguments (given 2, expected 1)
     #   ./spec/controllers/home_controller_spec.rb:12:in `block (2 levels) in <main>'

[2019-10-24 20:01:00.338] ERROR -- #<Double (anonymous)> received unexpected message :deliver_now with (no args): nil

(UserMailer (class)).welcome_email(#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
    expected: 1 time with arguments: (#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
    received: 2 times with arguments: (#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)

  0) User sends a welcome email on account creation
     Failure/Error: expect(UserMailer).to have_received(:welcome_email).with(user)

       (UserMailer (class)).welcome_email(#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
           expected: 1 time with arguments: (#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
           received: 2 times with arguments: (#<User id: 1, email: "tester44@example.com", created_at: "2019-10-24 11:01:00", updated_at: "2019-10-...rst_name: "Aaron", last_name: "Sumner", authentication_token: "9aDKu2QFE7M3DY_nQscq", location: nil>)
     # ./spec/models/user_spec.rb:32:in `block (2 levels) in <main>'
     # -e:1:in `<main>'

コントローラスペックの be_success を be_successful に変更する

Rails 5.2ではコントローラスペックで be_success を使うと以下のような警告が出ます。

DEPRECATION WARNING: The success? predicate is deprecated and will be removed in Rails 6.0. Please use successful? as provided by Rack::Response::Helpers. (called from matches? at rspec/rails/matchers/have_http_status.rb:263)

この警告に対応するため、 be_successful を使うようにします。

-expect(response).to be_success
+expect(response).to be_successful

参考 Rails 6 will remove success? predicate in favor of successful? · Issue #1857 · rspec/rspec-rails

コントローラスペックの have_content_type :json の記法を変更する

Rails 6からはコントローラスペックで expect(response).to have_content_type :json のようなエクスペクテーションを書いていると、以下のようにテストが失敗するようになりました。

Expected "unknown content type (application/json; charset=utf-8)" to be Content Type "application/json" (json)

そこでこのエクスペクテーションを次のように変更します。

-expect(response).to have_content_type :json
+expect(response.content_type).to eq "application/json; charset=utf-8"

参考:アプリ全体のdiffはこちら

Everyday RailsのサンプルアプリケーションをRails 6にアップグレードした際に発生するすべてのdiffについては以下のプルリクエストで確認できます。 何か不明な点があればこちらも参考にしてみてください。

github.com

Rails 6環境でうまく動かない場合

この記事を参考にしてもRails 6でサンプルアプリのテストがパスしない!という場合は、僕の方でできるかぎりサポートします。 ただし、その場合はTeratailやStackOverflowなど、「みんなが見える場所」で質問するようにしてください。(DMでの技術的な質問は基本的にNGです) その上でTwitter等で「ここに質問を書いたので見てください」とメンションをもらえれば、何らかのコメントを返すようにします。

なぜ「みんなが見える場所」での質問をお願いするのかという理由については以下のエントリにまとめてあるので、こちらも参考にどうぞ。

blog.jnito.com

その他、本書の内容と異なる部分

その他、Everyday Railsの2019-10-01版に記載した内容と、現在の情報で異なる部分を以下にピックアップしておきます。

システムスペックにジェネレータが追加された

Everyday Railsの付録Aでは「バージョン3.8.0の時点では、rspec-rails はシステムスペックを作成するジェネレータを提供していません」と書いてありますが、バージョン3.9.0からジェネレータが追加されました。

システムスペックのジェネレータは、以下のようなコマンドで起動することができます。

$ rails g rspec:system users

すると、以下のようなファイルが生成されます。

# spec/system/users_spec.rb 
require 'rails_helper'

RSpec.describe "Users", type: :system do
  before do
    driven_by(:rack_test)
  end

  pending "add some scenarios (or delete) #{__FILE__}"
end

ただし、Everyday Railsの場合、beforeブロックの中の driven_by(:rack_test) に相当する処理は spec/support/capybara.rb 内に書いてあります。 ですので、このbeforeブロックは削除して構いません。

RSpec本体とrspec-railsのバージョン番号が同期しなくなった

これまではRSpec本体(rspec gem)とrspec-railsは必ずバージョン番号が同期するようにリリースされていましたが、rspec-rails 4.0がリリースされるタイミングでRSpec本体とrspec-railsのバージョン番号が同期しなくなります。(rspec gemの最新版が3.9.xで、rspec-railsの最新版が4.0.xのような形になる)

これはrspec-railsの足並みをRSpec本体に合わせるよりも、Railsのアップデートに合わせるようにして、rspec-railsを頻繁にアップデートできるようにすることが目的のようです。

参考 RSpec 3.9 has been released, and RSpec team changes.

まとめ(というか結論):Everyday Railsの基本的な内容はほとんど古くなっていません!

というわけで、この記事ではEveryday RailsのサンプルアプリケーションをRails 6にアップグレードした際に発生する、テストコード関連の変更点についてまとめました。

Rails 5.1からRails 6にアップグレードするためにはgemのアップデート等、いろいろな作業が必要になりますが、 テストコードだけに着目すればそれほど大きな変更点はありません。 実際、テストコードで変更する必要があるのはfactory_botの記法と、コントローラスペックの一部だけです。

これはすなわち、Everyday Railsの内容が現在でもほとんど古くなっていないことを意味しています。 ですので、Everyday Railsでテストコードの書き方を学べば、Rails 6環境でも十分しっかりしたテストを書けるようになるはずです。

これからも安心してEveryday RailsをRSpecの参考書としてご活用ください!

「Everyday Rails - RSpecによるRailsテスト入門」について

「Everyday Rails - RSpecによるRailsテスト入門」はその名の通り、RailsアプリケーションをRSpecを使ってテストする方法を解説した電子書籍です。 難易度的には「RailsやRubyがある程度わかっている人であればOK、RSpecの知識はゼロでも大丈夫」というレベルになっています。

内容の詳細や購入方法等については以下のエントリにまとめてあるので、こちらを参考にしてください。 blog.jnito.com

ご購入は以下のサイトからどうぞ。 Everyday Rails - RSpecによるRailsテスト入門 - Leanpub f:id:JunichiIto:20190424050314p:plain