[Ruby on Rails チュートリアル][link-1]の第4版を学習し、[Everyday Rails - RSpecによるRailsテスト入門][link-2]でRSpecを学習したので、[Ruby on Rails チュートリアル][link-1]をRSpecでテストしてみました。
今回は第7章~第8章のテストを書きます。
第3章~第6章のテストは以下。
[Railsチュートリアルの第4版をRSpecでテスト-1][link-3]
[link-1]:https://railstutorial.jp/
[link-2]:https://leanpub.com/everydayrailsrspec-jp
[link-3]:https://qiita.com/yano12/items/eba8693272c31d6af7f0
[link-4]:https://qiita.com/yano12/items/4ef93129a50249adcb8d
[link-5]:https://qiita.com/yano12/items/319615e6e5fdcd7d844a
[link-6]:https://qiita.com/yano12/items/99e3fd4213f61339bad4
[link-7]:https://railstutorial.jp/chapters/account_activation?version=5.1#sec-exercises_mailer_templates
[link-8]:https://qiita.com/yano12/items/f5167416f3c20e1896d4
[link-9]:https://railstutorial.jp/chapters/password_reset?version=5.1#cha-password_reset
[link-10]:https://qiita.com/IrieLuka/items/34b0edccecc036d24748
ユーザーコントローラーのテスト
まず初めにユーザーコントローラーをテストしていきますが、[Everyday Rails - RSpecによるRailsテスト入門][link-2]によると
さあこれでみなさんはコントローラスペックとリクエストスペックの両方を書けるようになりました。では、どちらのテストを書くべきでしょうか?第5章でもお話ししたとおり、私はコントローラスペックよりも統合スペック(フィーチャスペックとリクエストスペック)を強くお勧めします。なぜならRails におけるコントローラスペックは重要性が低下し、かわりにより高いレベルのテストの重要性が上がってきているためです。こうしたテストの方がアプリケーションのより広い範囲をテストすることができます。
らしいので、今回はリクエストスペックでユーザーコントローラーのテストを書いていきます。
newアクションのテスト
初めにnewアクションをテストします。
spec/requests/user_spec.rbを作成し、新規登録ページが正しく表示されることをテストします。
require 'rails_helper'
RSpec.describe "User pages", type: :request do
let(:user) { FactoryBot.create(:user) }
# new
describe "GET #new" do
# 正常なレスポンスを返すこと
it "returns http success" do
get signup_path
expect(response).to be_success
expect(response).to have_http_status "200"
end
end
end
実行しテストがパスするのを確認します。
showアクションのテスト
続いてユーザー表示ページのテストですが、ログインしている場合のみ表示されることをテストするため、ログイン済みユーザーとゲストユーザー2通りのテストを書きます。
require 'rails_helper'
RSpec.describe "User pages", type: :request do
let(:user) { FactoryBot.create(:user) }
# newアクションのテストは省略
describe "GET #show" do
# ログイン済みのユーザーとして
context "as an authenticated user" do
# 正常なレスポンスを返すこと
it "responds successfully" do
sign_in_as user
get user_path(user)
expect(response).to be_success
expect(response).to have_http_status "200"
end
end
# ログインしていないユーザーの場合
context "as a guest" do
# ログイン画面にリダイレクトすること
it "redirects to the login page" do
get user_path(user)
expect(response).to redirect_to login_path
end
end
# アカウントが違うユーザーの場合
context "as other user" do
before do
@other_user = FactoryBot.create(:user)
end
# ホーム画面にリダイレクトすること
it "redirects to the login page" do
sign_in_as @other_user
get user_path(user)
expect(response).to redirect_to root_path
end
end
end
end
そしてsign_in_asメソッドをspec/support/utilities.rbで定義します。
# 他のメソッドは省略
# ログインする
def sign_in_as(user)
post login_path, params: { session: { email: user.email,
password: user.password } }
end
ここでテストを実行するとテストが失敗するので、ユーザーファクトリを修正しテストユーザーを有効化します。
FactoryBot.define do
factory :user do
name "Example"
sequence(:email) { |n| "tester#{n}@example.com" }
password "password"
password_confirmation "password"
# 有効化
activated true
end
end
テストを実行しパスするのを確認します。
ユーザー登録のテスト(※windowsでやると何故か失敗した)
続いてユーザー登録をテストしますが、私の場合、初めにwindowsのPCを使ってテストするとアカウント有効化の部分がパスせず、後日macでやると何故かパスしました。
開発環境はAWSのCloud9を使用しているのでOSは関係ないと思うのですが、理由がわかる方いましたら教えていただきたいです。
ユーザー登録のテストはフィーチャスペックに書いていきます。
フィーチャスペックでは、ブラウザの操作(リンクをクリックしたり、Webフォームを入力したり)をシミュレートするためにCapybaraを使います。
CpaybaraはRails5.1以後ではもとからインストールされていますが、インストールされていない場合は以下をGemfileに追加します。
group :test do
# 他のgemは省略
gem 'capybara', '>= 2.15'
end
また、rails_helper.rbでテストインストールに対してCapybaraを読み込むように伝えます。
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
require 'capybara/rspec'
# RSpecの設定が続く...
これでCapybaraを使う準備は完了です。
以下を実行してユーザー登録のテストを書くためのフィーチャスペックを作成します。
$ bin/rails g rspec:feature sign_up
次に、ユーザーがサインアップするためのステップと期待される結果をこのファイルに追加します。
require 'rails_helper'
RSpec.feature "SignUps", type: :feature do
include ActiveJob::TestHelper
# ユーザーはサインアップに成功する
scenario "user successfully signs up" do
visit root_path
click_link "新規会員登録"
click_link "メールアドレスで登録"
perform_enqueued_jobs do
expect {
fill_in "名前", with: "Example"
fill_in "メールアドレス", with: "test@example.com"
fill_in "パスワード", with: "test123"
fill_in "パスワード(確認)", with: "test123"
click_button "Sign up"
}.to change(User, :count).by(1)
expect(page).to have_content "アカウント有効化メールを送信しました。"
expect(current_path).to eq root_path
end
# 以下はアカウント有効化メールのテストです
# 詳細なテストは後で追加します
mail = ActionMailer::Base.deliveries.last
aggregate_failures do
expect(mail.to).to eq ["test@example.com"]
expect(mail.from).to eq ["noreply@example.com"]
expect(mail.subject).to eq "Account activation"
end
end
end
また、アカウント有効化メールをテストするためにアプリケーションのdevelopment環境とtest環境の設定を追加します。
Rails.application.configure do
.
.
.
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :test
host = 'example.com' # ホスト名
config.action_mailer.default_url_options = { host: host, protocol: 'https' }
.
.
.
end
Rails.application.configure do
.
.
.
config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: 'example.com' } #自分のホスト名
.
.
.
end
テストを実行すると初めは以下のエラーが出ていましたが、数日後にmacでテストするとパスしました。
Failures:
1) SignUps user successfully signs up
Failure/Error: <%= link_to "Activate", edit_account_activation_url(@user.activation_token, email: @user.email) %>
ActionView::Template::Error:
Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
サインインのテスト
ユーザーのログインテストはログイン成功時と失敗時の2つのテストを書いていきます。
spec/features/login_spec.rbを作成し以下を追加します。
require 'rails_helper'
RSpec.feature "Login", type: :feature do
let(:user) { FactoryBot.create(:user) }
# ログインに成功すること
scenario "user successfully login" do
valid_login(user)
expect(current_path).to eq user_path(user)
expect(page).to_not have_content "ログインする"
end
# 無効な情報ではログインに失敗すること
scenario "user doesn't login with invalid information" do
visit root_path
click_link "ログイン"
fill_in "メールアドレス", with: ""
fill_in "パスワード", with: ""
click_button "ログインする"
expect(current_path).to eq login_path
expect(page).to have_content "ログインする"
expect(page).to have_content "メールアドレスまたはパスワードが正しくありません。"
end
end
また、spec/support/login_support.rbを作成し、valid_loginメソッドを追加します。
サポートモジュールを使うことでコードの重複を失くし、ログイン処理が変わった場合でも全てのテストコードを変更する必要がなくなるので便利です。
# 新しいモジュールを作成
module LoginSupport
def valid_login(user)
visit root_path
click_link "ログイン"
fill_in "メールアドレス", with: user.email
fill_in "パスワード", with: user.password
click_button "ログインする"
end
end
# LoginSupportモジュールをincludeする
RSpec.configure do |config|
config.include LoginSupport
end
モジュールの定義の後にはRSpecの設定が続きます。
ここでは、RSpec.configureを使って新しく作ったモジュール(LoginSupport)をinculdeしています。
テストを実行しパスするのを確認します。
サインアウトのテスト
サインインに続いてサインアウトのテストを書いていきます。
先ほどのspec/features/login_spec.rbにログアウトのテストを追加します。
require 'rails_helper'
RSpec.feature "Login", type: :feature do
let(:user) { FactoryBot.create(:user) }
# ログインに成功しログアウトすること
scenario "user successfully login" do
valid_login(user)
expect(current_path).to eq user_path(user)
expect(page).to_not have_content "ログイン"
# ログアウトのテスト
click_link "ログアウト"
expect(current_path).to eq root_path
expect(page).to have_content "ログイン"
end
# ・・・以下省略
テストを実行しパスするのを確認します。
今回は以上です。
第9章以降は完成し次第投稿する予定です。
#追記
第9章以降のテストを書きました。
- [Railsチュートリアルの第4版をRSpecでテスト-3][link-5](第9章)
- [Railsチュートリアルの第4版をRSpecでテスト-4][link-6](第10章)
- [Railsチュートリアルの第4版をRSpecでテスト-5][link-8](第11章)
- [Railsチュートリアルの第4版をRSpecでテスト-6][link-10](第12章)