はじめに
今回もRuby on Railsの自動テストについて学んでいきます。
前回に引き続きタスク管理アプリにシステムテストを実装していきます。
尚、自動テストについては今回で最後になります。
shared_examplesの利用
現在のtasks_spec.rbのコードを見ると、一覧表示機能のユーザーAがログインしている時のcontext内のitと、詳細表示機能のユーザーAがログインしている時のcontext内のitのコードは同じでどちらも次のようになっています。
spec/system/tasks_spec.rb
it 'ユーザーAが作成したタスクが表示される' do
expect(page).to have_content '最初のタスク'
end
よりDRYなコードにするためにこの部分を共通化します。
Rspecではitを共通化する方法としてshared_examplesという仕組みを用意しています。
exampleはitなどの期待する挙動を示す部分のことです。このexampleをいくつかまとめて名前をつけ、テストケース間でシェアできます。
ここでは重複しているitを含むshared_examplesを作ります。shared_examplesの名前は「ユーザーAが作成したタスクが表示される」とします。
shared_examples_for 'ユーザーAが作成したタスクが表示される' do
it { expect(page).to have_content '最初のタスク' }
end
次にここで共通化したitの処理が書かれていた箇所を、このshared_examplesを利用するという次の処理に置き換えます。
it_behaves_like 'ユーザーAが作成したタスクが表示される'
これらの変更を加えたtasks_spec.rbは以下の通りです。
spec/system/tasks_spec.rb
require 'rails_helper'
describe 'タスク管理機能', type: :system do
let(:user_a) { FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com') }
let(:user_b) { FactoryBot.create(:user, name: 'ユーザーB', email: 'b@example.com') }
let!(:task_a) { FactoryBot.create(:task, name: '最初のタスク', user: user_a) }
before do
FactoryBot.create(:task, name: '最初のタスク', user: user_a)
visit login_path
fill_in 'メールアドレス', with: login_user.email
fill_in 'パスワード', with: login_user.password
click_button 'ログインする'
end
shared_examples_for 'ユーザーAが作成したタスクが表示される' do
it { expect(page).to have_content '最初のタスク' }
end
describe '一覧表示機能' do
context 'ユーザーAがログインしている時' do
let(:login_user) { user_a }
it_behaves_like 'ユーザーAが作成したタスクが表示される'
end
context 'ユーザーBがログインしているとき' do
let(:login_user) { user_b }
it 'ユーザーAが作成したタスクが表示されない' do
expect(page).to have_no_content '最初のタスク'
end
end
end
describe '詳細表示機能' do
context 'ユーザーAがログインしている時' do
let(:login_user) { user_a }
before do
visit task_path(task_a)
end
it_behaves_like 'ユーザーAが作成したタスクが表示される'
end
end
end
テストを実行すると無事成功します。
$ bundle exec rspec spec/system/tasks_spec.rb
...
Finished in 11.48 seconds (files took 10.89 seconds to load)
3 examples, 0 failures
これでitの処理を共通化できました。
新規作成機能のSystem Spec
タスクの新規作成機能のSystem Specを記述します。
タスク管理アプリにはタスクの名称を入力しないと登録できないよう検証機能を追加済みなので、タスク名称を入力すると登録ができること、タスク名称を入力しないと検証エラーになることの2つのテストケースを記述します。
tasks_spec.rbを以下のように編集します。
spec/system/tasks_spec.rb
require 'rails_helper'
describe 'タスク管理機能', type: :system do
let(:user_a) { FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com') }
let(:user_b) { FactoryBot.create(:user, name: 'ユーザーB', email: 'b@example.com') }
let!(:task_a) { FactoryBot.create(:task, name: '最初のタスク', user: user_a) }
before do
FactoryBot.create(:task, name: '最初のタスク', user: user_a)
visit login_path
fill_in 'メールアドレス', with: login_user.email
fill_in 'パスワード', with: login_user.password
click_button 'ログインする'
end
shared_examples_for 'ユーザーAが作成したタスクが表示される' do
it { expect(page).to have_content '最初のタスク' }
end
describe '一覧表示機能' do
context 'ユーザーAがログインしている時' do
let(:login_user) { user_a }
it_behaves_like 'ユーザーAが作成したタスクが表示される'
end
context 'ユーザーBがログインしているとき' do
let(:login_user) { user_b }
it 'ユーザーAが作成したタスクが表示されない' do
expect(page).to have_no_content '最初のタスク'
end
end
end
describe '詳細表示機能' do
context 'ユーザーAがログインしている時' do
let(:login_user) { user_a }
before do
visit task_path(task_a)
end
it_behaves_like 'ユーザーAが作成したタスクが表示される'
end
end
describe '新規作成機能' do
let(:login_user) { user_a }
before do
visit new_task_path
fill_in '名称', with: task_name // ①
click_button '登録する'
end
context '新規作成画面で名称を入力したとき' do
let(:task_name) { '新規作成のテストを書く' }
it '正常に登録される' do
expect(page).to have_selector '.alert-success', text: '新規作成のテストを書く' // ②
end
end
context '新規作成画面で名称を入力しなかったとき' do
let(:task_name) { '' }
it 'エラーとなる' do
within '#error_explanation' do // ③
expect(page).to have_content '名称 を入力してください'
end
end
end
end
end
コードの中身を見ると、①の箇所ではtask_nameというletを利用して続くcontextで名称を入力するかしないかの違いを吸収します。
②では、have_selectorというマッチャを使って、alert-successというCSSクラスのついた、テキストに「新規作成のテストを書く」を含む要素があるか検査しています。
③ではwithinを用いて、error_explanationというidの要素内に探索する範囲を狭めて、「名称 を入力してください」というエラーメッセージが含まれているかどうかを調べています。
テストを実行すると無事成功します。
$ bundle exec rspec spec/system/tasks_spec.rb
.....
Finished in 7.54 seconds (files took 2.53 seconds to load)
5 examples, 0 failures
新規作成機能のSystemSpecを作成できました。
letの上書き
これまでで、letは利用する箇所の外側でも、内側でも定義できるということを説明しましたが、もう一つ重要な点が処理経路上に複数回、同じ名前のletを定義できるということです。
require 'rails_helper'
describe 'タスク管理機能', type: :system do
let(:user_a) { FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com') }
let(:user_b) { FactoryBot.create(:user, name: 'ユーザーB', email: 'b@example.com') }
let!(:task_a) { FactoryBot.create(:task, name: '最初のタスク', user: user_a) }
before do
FactoryBot.create(:task, name: '最初のタスク', user: user_a)
visit login_path
fill_in 'メールアドレス', with: login_user.email
fill_in 'パスワード', with: login_user.password
click_button 'ログインする'
end
shared_examples_for 'ユーザーAが作成したタスクが表示される' do
it { expect(page).to have_content '最初のタスク' }
end
・・・
describe '新規作成機能' do
let(:login_user) { user_a }
let(:task_name) { '新規作成のテストを書く' } // デフォルトの設定
before do
visit new_task_path
fill_in '名称', with: task_name
click_button '登録する'
end
context '新規作成画面で名称を入力したとき' do
it '正常に登録される' do
expect(page).to have_selector '.alert-success', text: '新規作成のテストを書く'
end
end
context '新規作成画面で名称を入力しなかったとき' do
let(:task_name) { '' } // 上書き
it 'エラーとなる' do
within '#error_explanation' do
expect(page).to have_content '名称 を入力してください'
end
end
end
end
end
処理経路上に複数回、同じ名前のletを定義したときは、常に下の階層に定義したletが使われます。つまりletは上書きできます。
上記のコードでは、新規作成機能で利用するtask_nameをデフォルトでは「新規作成のテストを書く」にしておき、名称を入力しないテストケースでは値をから文字列に上書きしています。
このSpecを実行すると全て成功します。
$ bundle exec rspec spec/system/tasks_spec.rb
.....
Finished in 10.13 seconds (files took 3.07 seconds to load)
5 examples, 0 failures
しかしletの上書きを多用すると、Specのコード行数が多くなったときに、どのletが最終的に使われるのかわかりづらくなるという難点があります。
適切なバランスで利用する必要があります、
終わりに
ここまででSystem Specを書くために必要となる基本知識を学び、タスク管理アプリの一覧表示機能、詳細表示機能、新規作成機能について簡単なSystem Specを作成しました。
今回でRailsの自動テストについてのまとめは最後になります。
コメント