【RSpec】System Spec
- やりたいこと
- 前提
- 実装の流れ
- Gem導入
- SystemSpecファイルの作成
- ドライバの設定
- モジュールの設定
- タグの設定
- SystemSpecファイルの設定
- Rspecの実行
- RSpecの結果出力を見易く表示する。
- 参考
やりたいこと
タスクとユーザーのCRUD機能について、テストケースを作成したい。
詰まった部分だけ復習していきます。
前提
実装の流れ
- 必要なgemの導入
- SystemSpecファイルの作成
- ドライバの設定
- モジュールの設定
- SystemSpecファイルの設定
Gem導入
必要なgemを導入
gem 'webdrivers' #ブラウザの挙動を確認 gem 'capybara' #E2Eテスト用フレームワーク
require 'capybara/rspec'
SystemSpecファイルの作成
% rails g rspec:system tasks
※必要に応じて、users
とuser_sessions
も作成。
ドライバの設定
ドライバとは、Capybaraを使ったテスト/Specにおいて、ブラウザ相当の機能を利用するために必要なプログラムです。
『現場で使える Ruby on Rails5 速習実践ガイド』P193
RSpec.configure do |config| config.before(:each, type: :system)do driven_by(:selenium_chrome_headless) #ドライバを一元指定 end
モジュールの設定
今回、ログイン前・ログイン後に分けてテストを実施します。
モジュールにログイン処理を設定しておきます。
そうすれば、各Specで呼び込むことができるので、コードがすっきりします。
どこにファイルを置くか迷ったのですが、support
ディレクトリ内にします。
module LoginModule def login(user) visit login_path fill_in 'Email', with: user.email fill_in 'Password', with: 'password' click_button 'Login' end end
モジュールを作っただけでは使用できないので、include
する必要があります。
config.include FactoryBot::Syntax::Methods config.include LoginModule #追記
そしてrails_helper.rb
を読み込むために、各Specファイルには、上部にrequire 'rails_helper'
があります。
require 'rails_helper'
モジュールを読みこめない…
以上のように設定してから、SystemSpecファイル内でlogin
メソッドを呼び出そうとしたのですが、読み込めませんでした…
includeもOK requireもOKなのに、なぜ・・・?
An error occurred while loading ./spec/system/users_spec.rb. Failure/Error: config.include LoginModule
パスを通してなかった。spec/support
以下を読み込むように設定する。
以下のコメントアウトを外して有効化します。
# The following line is provided for convenience purposes. It has the downside # of increasing the boot-up time by auto-requiring all files in the support # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } #コメントアウトを外す
タグの設定
タグの機能を使うと、タグを設定したテストだけ実行してくれます。
:focus
タグを付けたテストが何もないときは、全てのテストを実行します。
# This allows you to limit a spec run to individual examples or groups # you care about by tagging them with `:focus` metadata. When nothing # is tagged with `:focus`, all examples get run. RSpec also provides # aliases for `it`, `describe`, and `context` that include `:focus` # metadata: `fit`, `fdescribe` and `fcontext`, respectively. config.filter_run_when_matching :focus #コメントアウトを外す。
SystemSpecファイルの設定
全部書いてると大量になってしまうので、詰まった部分だけ見直していきます。
require 'rails_helper' RSpec.describe "Users", type: :system do let(:user) { create(:user) } let(:other_user) { create(:user) } describe 'ログイン前' do describe 'ユーザー新規登録' do context 'フォームの入力値が正常' do it 'ユーザーの新規作成が成功する' end context 'メールアドレスが未入力' do it 'ユーザーの新規作成が失敗する' end context '登録済のメールアドレスを使用' do it 'ユーザーの新規作成が失敗する' do existed_user = create(:user) visit sign_up_path fill_in 'Email', with: existed_user.email fill_in 'Password', with: 'password' fill_in 'Password confirmation', with: 'password' click_button 'SignUp' expect(page).to have_content '1 error prohibited this user from being saved' expect(current_path).to eq users_path expect(page).to have_content("Email has already been taken") expect(page).to have_field 'Email', with: existed_user.email #登録を受け付けなくても、入力したメールアドレスが残ってる。 end end end end describe 'ログイン後' do before { login(user) } #設定したモジュールのloginメソッドを呼び出す describe 'ユーザー編集' do context 'フォームの入力値が正常' do it 'ユーザーの編集が成功する' end context 'メールアドレスが未入力' do it 'ユーザーの編集が失敗する' end context '登録済みのメールアドレスを使用' do it 'ユーザーの編集が失敗する' do visit edit_user_path(user) fill_in 'Email', with: other_user.email fill_in 'Password', with: 'password' fill_in 'Password confirmation', with: 'password' click_button 'Update' expect(page).to have_content('1 error prohibited this user from being saved') expect(page).to have_content("Email has already been taken") expect(current_path).to eq user_path(user) end end context '他ユーザーの編集ページにアクセス' do it '編集ページへのアクセスが失敗する' do visit edit_user_path(other_user) #userでログインしている。other_userの編集ページへアクセスする expect(current_path).to eq user_path(user) expect(page).to have_content("Forbidden access.") end end end describe 'マイページ' do context 'タスクを作成' do it '新規作成したタスクが表示される' do create(:task, title:'test', status: :doing, user: user) visit user_path(user) expect(page).to have_content('You have 1 task.') expect(page).to have_content('test') expect(page).to have_content('doing') expect(page).to have_link('Show') expect(page).to have_link('Edit') expect(page).to have_link('Destroy') end end end end end
require 'rails_helper' RSpec.describe "Tasks", type: :system do let(:user) { create(:user) } #taskとuserは紐付いているので、両方作成する。 let(:task) { create(:task) } describe 'ログイン前' do describe 'ページの遷移確認' do context 'タスクの新規登録ページにアクセス' do it '新規登録ページのアクセス失敗' end context 'タスクの編集ページにアクセス' it '編集ページのアクセス失敗' end context 'タスクの一覧ページにアクセス' do it '全てのユーザーのタスク情報が表示される' do task_list = create_list(:task, 3) visit tasks_path expect(page).to have_content task_list[0].title expect(page).to have_content task_list[1].title expect(page).to have_content task_list[2].title expect(current_path).to eq tasks_path end end context 'タスクの詳細ページにアクセス' do it 'タスク詳細ページを見られること' do visit task_path(task) expect(current_path).to eq task_path(task) expect(page).to have_content task.title end end end describe 'ログイン後' do before { login(user) } describe 'タスク新規登録' do context 'フォームの入力値が正常' do it 'タスクの新規作成が成功する' do visit new_task_path fill_in 'Title', with: 'test_title' fill_in 'Content', with: 'test_content' select 'doing', from: 'Status' fill_in 'Deadline', with: DateTime.new(2020, 9, 28, 10, 30) click_button 'Create Task' expect(page).to have_content 'Title: test_title' #作成した内容が表示される expect(page).to have_content 'Content: test_content' expect(page).to have_content 'Status: doing' expect(page).to have_content 'Deadline: 2020/9/28 10:30' expect(current_path).to eq '/tasks/1' end end context 'タイトルが空白' do it 'タスクの新規登録が失敗すること' end context '登録済のタイトルを入力' do it 'タスクの新規作成が失敗する' do visit new_task_path other_task = create(:task) fill_in 'Title', with: other_task.title fill_in 'Content', with: 'test_content' click_button 'Create Task' expect(page).to have_content '1 error prohibited this task from being saved' expect(page).to have_content 'Title has already been taken' expect(current_path).to eq tasks_path end end end describe 'タスク編集' do let!(:task) { create(:task, user: user) } #letの遅延読み込みではなく、各テストが読み込まれる前に作成する。 let!(:other_task) { create(:task, user: user) } before { visit edit_task_path(task) } #編集ページに遷移しておく。 context 'フォームの入力値が正常' do it 'タスクの更新に成功すること' do fill_in 'Title', with: 'title_test' select :done, from: 'Status' click_button "Update Task" expect(current_path).to eq task_path(task) expect(page).to have_content("Task was successfully updated") end end end describe 'タスク削除' do let!(:task) { create(:task, user: user) } it 'タスクの削除ができること' do visit tasks_path click_link "Destroy" expect(page.accept_confirm).to eq 'Are you sure?' expect(current_path).to eq tasks_path expect(page).to have_content("Task was successfully destroyed") expect(page).not_to have_content task.title #削除したタスクが表示されてないことを確認 end end end end
create_listで連続するテストデータを作成する。
FactoryBot.create_list(:task,3)
FactoryBot
を省略できる設定をしてるのでこのように書ける。
task_list = create_list(:task, 3)
letとlet!の違い
let
は遅延読み込みなので、userやtaskが必要になったときだけ作成します。
一方let!
は、各テストのブロックが実行される前に作成します。
今回、編集ページのテストを行うには、予めタスクが必要なため、let!
で作成しておきます。
let!(:task) { create(:task, user: user) } let!(:other_task) { create(:task, user: user) } before { visit edit_task_path(task) }
エラーが起こったところ
describe 'マイページ' do context 'ログインしていない状態' do it 'マイページへのアクセスが失敗する' do visit user_path expect(current_path).to eq login_path end end end
1) Users ログイン前 マイページ ログインしていない状態 マイページへのアクセスが失敗する Failure/Error: visit user_path ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"users"}, missing required keys: [:id]
[:id]
がないよって怒られる。
user_path(user)
が抜けてた。
(user)
忘れがちなので注意する。
Rspecの実行
% bundle exec rspec spec/system/tasks_spec.rb
RSpecの結果出力を見易く表示する。
以下のオプションを追記する。
--require spec_helper --format documentation #追記
参考
『現場で使える Ruby on Rails5 速習実践ガイド』P184
『everyday Rails』