Help us understand the problem. What is going on with this article?

【動画付き】Railsチュートリアルの統合テスト(integration test)は、RSpecのリクエストスペックに置き換えるのがラクです

More than 1 year has passed since last update.

はじめに

さきほど、こちらの記事を拝見しました。

RSpecのfeatureテストでsessionを扱う方法 - Qiita

詳しい内容はリンク先をチェックしていただきたいのですが、ざっくりまとめると、

  • RailsチュートリアルのテストをRSpecのフィーチャスペックに置き換えようとした
  • Railsチュートリアルのテストではsession変数を操作するヘルパーメソッドを使っていたが、フィーチャスペックではその方法がわからなかった
  • rack_session_accessというgemを使ったら、フィーチャスペックからsession変数を操作できた

というお話です。

ただ、僕はこの記事を読んで「うーん、僕はフィーチャスペックの中でわざわざsession変数を操作することはしないなあ」と思いました。

というわけで、この記事では「じゃあどうするのがベストだったのか?」という点を議論してみたいと思います。

TL; DR(最初に結論)

「どうするのがベストだったのか?」という問いに対する僕の回答は以下のようになります。

  • Rails標準の統合テスト(integration test)は、RSpecにおけるリクエストスペックに対応するものだと考えるのが良い。なので、フィーチャスペックよりもリクエストスペックに置き換える方がラクである。
  • どうしてもフィーチャスペック(もしくはシステムスペック)に置き換えたい場合は、テストの視点を「ブラウザを操作するユーザーの視点」に切り替える必要がある。
  • つまり、「統合テストでやっているテストはブラウザ上でどう操作するか?または、どう見えるか?」を頭の中で翻訳しなければいけないので、(不可能ではないものの)初心者には少しハードルが高い。

Railsチュートリアルに載っているテストコード

今回、Railsチュートリアルのテストコードはこちらのページで公開されているものを使います。

test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest
  # 省略

  test "valid signup information" do
    get signup_path
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    # redirect_to @user
    follow_redirect!
    # Test
    assert_template 'users/show'
    assert is_logged_in?
  end
end

リクエストスペックで置き換えてみる

統合テストはリクエストスペックで置き換えるのがラクです。
リクエストスペックで書けば、統合テストとほとんど違いがありません。

spec/requests/users_signup_spec.rb
require "rails_helper"

RSpec.describe "Users signup", type: :request do
  example "valid signup information" do
    get signup_path
    expect {
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    }.to change(User, :count).by(1)
    redirect_to @user
    follow_redirect!
    # Test
    assert_template 'users/show'
    assert is_logged_in?
  end
end

is_logged_in?メソッドは次のように定義します。
リクエストスペックであればsession変数もそのまま使えます。

spec/support/integration_helpers.rb
module IntegrationHelpers
  def is_logged_in?
    !session[:user_id].nil?
  end
end
spec/rails_helper.rb
# (省略)
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# (省略)
RSpec.configure do |config|
  # (省略)
  config.include IntegrationHelpers, type: :request
end

補足1

ここではあまり深く考えずにIntegrationHelpersという名前を使いましたが、リクエストスペック用のヘルパーメソッドであれば、RequestHelpersみたいな名前の方が良かったかもしれません😓

補足2

assert_templateassertがRSpecでも動いてしまったので気づきませんでしたが、RSpecらしく書くのであれば次のように書いた方が良いと思います。

expect(response).to render_template('users/show')
expect(is_logged_in?).to be_truthy

システムスペックで置き換える場合

冒頭に紹介した記事では統合テストからフィーチャスペックに置き換えようとしていましたが、新たにE2Eテストを書くのであれば、システムスペックを使う方がいいでしょう(今後フィーチャスペックはシステムスペックに置き換わっていくため)。
というわけで、この記事ではシステムスペックで置き換えることにします。

システムスペック(またはフィーチャスペック)を書く場合は、「ブラウザを操作するユーザーの気持ち」になってテストを書くことが重要です。
基本的な考え方としては、「システムスペックはブラウザ上でできることにフォーカスしてテストを書く(やむを得ない場合のみ、サーバーサイドの処理やデータを覗き見る)」です。
ですので、session変数を直接読み書きするようなテストコードは通常書きません。

以下は先ほどのテストコードをフィーチャスペックで置き換える例です。

users_signup_spec.rb
require "rails_helper"

RSpec.describe "Users signup", type: :system do
  example "valid signup information" do
    # ユーザー登録画面を開く
    visit signup_path

    # 必要事項をフォームに入力する
    fill_in "Name", with: "Example User"
    fill_in "Email", with: "user@example.com"
    fill_in "Password", with: "password"
    fill_in "Confirmation", with: "password"

    # 登録ボタンをクリックすると、Userが1件増える
    expect {
      click_button "Create my account"
    }.to change(User, :count).by(1)

    # ユーザー詳細画面に遷移する
    user = User.last
    expect(current_path).to eq user_path(user)

    # Welcomeメッセージが表示される
    expect(page).to have_content "Welcome to the Sample App!"

    # ナビゲーションバー内の表示が"Log in"から"Account"に切り替わる
    within '.navbar-nav' do
      expect(page).to_not have_content 'Log in'
      expect(page).to have_content 'Account'
    end
  end
end

ここでは、ログインが完了したことを検証するために、「ナビゲーションバー内の表示が"Log in"から"Account"に切り替わる」を検証することにしました。
(システムスペック上ではsession変数の中身は意識しません)

# ナビゲーションバー内の表示が"Log in"から"Account"に切り替わる
within '.navbar-nav' do
  expect(page).to_not have_content 'Log in'
  expect(page).to have_content 'Account'
end

繰り返しになりますが、統合テストをシステムスペックに移行する場合は、「統合テスト内で確認していた内容を、ブラウザ上ではどう検証するか」を考える必要があります。
この翻訳作業が初心者さんにとっては、ちょっと難しい作業になるんじゃないかと僕は考えています。

参考情報

今回使用したサンプルコードはGitHubにアップしています。
以下のURLでdiffを確認できます。

https://github.com/yasslab/sample_apps/compare/master...JunichiIto:use-rspec?expand=1

また、解説動画もYouTubeにアップしています。
こっちの方が説明が詳しいと思うので、動画もぜひチェックしてみてください。

https://www.youtube.com/watch?v=-PAyalRKVW8
image

フィーチャスペックやシステムスペックの書き方については(そしてリクエストスペックも!)、電子書籍「Everyday Rails - RSpecによるRailsテスト入門」を読んで勉強するのがオススメです。

https://leanpub.com/everydayrailsrspec-jp

フィーチャスペックやシステムスペックでブラウザ上の操作をどうやって再現するかについては、以下のQiita記事も参考になります。

使えるRSpec入門・その4「どんなブラウザ操作も自由自在!逆引きCapybara大辞典」 - Qiita

jnchito
SIer、社内SEを経て、ソニックガーデンに合流したプログラマ。 「プロを目指す人のためのRuby入門」の著者。 http://gihyo.jp/book/2017/978-4-7741-9397-7 および「Everyday Rails - RSpecによるRailsテスト入門」の翻訳者。 https://leanpub.com/everydayrailsrspec-jp
https://blog.jnito.com/
sonicgarden
「お客様に無駄遣いをさせない受託開発」と「習慣を変えるソフトウェアのサービス」に取り組んでいるソフトウェア企業
http://www.sonicgarden.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした