はじめに
プログラミングスクールにてRuby on Railsを学習している者です。
この記事では、System Specの書き方で疑問に思った点と自分なりの解釈をまとめます。
初学者(学習期間1ヶ月)のため、初歩的な内容です。
誤った記述があるかもしれませんが、ご指摘いただけますと幸いです。
よろしくお願いいたします。
前提
タスク管理アプリの一覧画面にて、タスク登録日時の降順表示(最新のものが上にくる)に切り替えた際のテストコードです。
task_spec.rb
require 'rails_helper'
RSpec.describe 'タスク管理機能', type: :system do
let!(:task) {FactoryBot.create(:task, name: 'aaa')}
describe '一覧表示機能' do
before do
task1 = FactoryBot.create(:task, name: 'bbb')
task2 = FactoryBot.create(:task, name: 'ccc')
visit tasks_path
end
context '一覧画面に遷移した場合' do
it '作成済みのタスク一覧が表示される' do
expect(page).to have_content 'bbb'
expect(page).to have_content 'ccc'
end
end
context 'タスクが作成日時の降順に並んでいる場合' do
it '新しいタスクが一番上に表示される' do
expect(page.text).to match(/#{task2.name}[\s\S]*#{task1.name}/)
end
end
end
end
factories/tasks.rb
FactoryBot.define do
factory :task do
name { 'test_name' }
detail { 'test_detail' }
end
end
エラー発生
上記のコードを実行すると、it '新しいタスクが一番上に表示される' do の部分で task1 って何?taskのことですか? とエラーが出ました。
Failure/Error: expect(page.text).to match(/#{task2.name}[\s\S]*#{task1.name}/)
NameError:
undefined local variable or method `task1' for #<RSpec::ExampleGroups::Nested::Nested_2::Nested_2 "新しいタスクが一番上に表示される" (./spec/system/task_spec.rb:29)>
Did you mean? task
しかし、it '作成済みのタスク一覧が表示される' do のテストはパスしているため、
beforeの部分のcreateは成功しているはずです。
では、なぜtask1が定義されていないと怒られてしまうのでしょう?
それもそのはず。インスタンス変数でないと、メソッド外で呼び出せません。
下記の通り、beforeの定義の際に、データをインスタンス変数に代入することで、テスト成功。
before do
@task1 = FactoryBot.create(:task, name: 'bbb')
@task2 = FactoryBot.create(:task, name: 'ccc')
visit tasks_path
end
さらに疑問に思ったこと
先ほどのエラーメッセージでtask1じゃなくて、taskじゃないですか?と言われておりました。
Failure/Error: expect(page.text).to match(/#{task2.name}[\s\S]*#{task1.name}/)
NameError:
undefined local variable or method `task1' for #<RSpec::ExampleGroups::Nested::Nested_2::Nested_2 "新しいタスクが一番上に表示される" (./spec/system/task_spec.rb:29)>
Did you mean? task
このtaskって何?逆にそのtaskはどこに存在しているのでしょう?
自分なりの解釈
自分で書いたコードなのに自信がありません。
let!で定義している変数taskのことでしょうか。
let!(:task) {FactoryBot.create(:task, name: 'aaa')}
試しに binding.irb で確認した所、taskにはname'aaa'のデータが入っていました。
let!で定義した変数はインスタンス変数としてどこでも使えるのですね。
@はついてないけど・・・・・。
おまけ
今回のコードでは、
let! で name'aaa' のデータを作成
↓
before で name'bbb' のデータを作成
↓
before で name'ccc' のデータを作成
この順番でデータが作成されました。
ということは、なんとなくイメージ被りしているlet!とbeforeですが、let!のほうが実行順位が高いんですね。いや、let!のほうがネストの浅いところに記述しているので当然・・・?
今後もう少しコードを動かして考えてみようと思います。
※コードの中に不要なlet!が存在しており気持ち悪く感じられた方がいらっしゃいましたら申し訳ございません。let!は今回記載していない別の機能でのみ必要でしたが、エラー文に関係していたためコードを残しました。
参考にさせていただいた記事
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」
【Rspec】system testで並び順をテストする
【Rspec】letとlet!、beforeの違い
RSpec letとlet!とbeforeの実行されるタイミング.
ありがとうございました。