letを使ったほうがいいはあるが、インスタンス変数を使うデメリットに関してはあんまりないので書いてみる。
環境
$ bundle exec rspec -v RSpec 3.8 - rspec-core 3.8.0 - rspec-expectations 3.8.2 - rspec-mocks 3.8.0 - rspec-rails 3.8.1 - rspec-support 3.8.0
今回想定するケース
対象となるモデル
# == Schema Information # # Table name: users # # id :bigint(8) not null, primary key # name :string(255) # created_at :datetime not null # updated_at :datetime not null # class User < ApplicationRecord end
インスタンス変数を使う場合
describe "#create" do before do @user = User.create(name: "Taro") end it "正しくcreateできる" do expect(@user.present?).to eq(true) end end
インスタンス変数を使わない場合
describe "#create" do let(:user) {User.create(name: "Taro")} it "正しくcreateできる" do expect(user.present?).to eq(true) end end
インスタンス変数を見直したがほうがいい理由
letに変えると遅延評価になるので、利用されないシーンでは無駄に呼ばれることがない
let
だと必要なタイミングで呼び出されるが、before
でのインスタンス変数定義だと全ての場合に呼び出される。そのため context
などで場合わけしたときに不要な場面でも作られることになる。
RSpecの場合、レコード生成やDBアクセスが実行時間に影響するケースが多いのでなるべくであれば生成する契機は少なくしたほうがいいので、そのような観点でもインスタンス変数は避けたほうがいい。
インスタンス変数だとnilとしてテストが実行される
例えば例に上げたインスタンス変数版のテストで、インスタンス変数名を間違えているとする
describe "#create" do before do # @usersにタイポ @users = User.create(name: "Taro") end it "正しくcreateできる" do expect(@user.present?).to eq(true) end end
そうすると nil になってテストが実行される。
$ bundle exec rspec spec/models/user_spec.rb F Failures: 1) User#create 正しくcreateできる Failure/Error: expect(@user.present?).to eq(true) expected: true got: false (compared using ==) # ./spec/models/user_spec.rb:28:in `block (3 levels) in <top (required)>' Finished in 0.01941 seconds (files took 1.25 seconds to load) 1 example, 1 failure
(例としては微妙だが)たとえば @user.nameがnilである
というようなテストをぼっち演算子つかった場合は意図と反して通ってしまう
describe "#create" do before do # user を users にタイポ @users = User.create(name: nil) end it "nameはnil" do expect(@user&.name).to eq(nil) end end
letを使うと user
が定義されていないでテストが失敗するので気づける
describe "#create" do let(:users) {User.create(name: nil)} it "nameはnil" do expect(user&.name).to eq(nil) end end
$ bundle exec rspec spec/models/user_spec.rb F Failures: 1) User#create nameはnil Failure/Error: expect(user&.name).to eq(nil) NameError: undefined local variable or method `user' for #<RSpec::ExampleGroups::User::Create:0x00007f8fbb2c4b68> Did you mean? users # ./spec/models/user_spec.rb:18:in `block (3 levels) in <top (required)>'