RSpecのletとlet!の違い

RSpecのletとlet!の違い

let = 遅延読み込み

let! = ブロックを即座に実行

実際の挙動をまとめてみます。

let

RSpec.feature "Users", type: :feature do    
  let(:user) { User.create(                 
                first_name: "tarou",        
                last_name: "suzuki",        
                email: "test@example.com",  
                password: "password"        
  )}                                        
                                            
  context "when letのとき" do               
    it "is valid" do                        
      expect(user).to be_valid              
    end                                     
  end                                       

letで事前に定義したuserがexpect(user)で実行する際に、初めて定義されます。

Before(:each)と違い、それぞれのブロック前に定義されるのではなく、呼び出した際に初めて定義されます。 そのため、時間がかかるものをbeforeで定義した場合にテスト全体の負荷が増える可能性が高いです。(letでは必要がある際に、呼び出しその都度定義するため負荷が軽い)

また、beforeではインスタンス変数(@user等)でしたが、letの場合はローカル変数として扱います。

let!

RSpec.feature "Users", type: :feature do   
  let(:user) { User.create(                
                first_name: "tarou",       
                last_name: "suzuki",       
                email: "test@example.com", 
                password: "password"       
  )}                                       
                                           
  let(:project) { Project.create(          
                name: "test_project",      
                owner: user                
  )}                                       
                                           
  context "when letのとき" do              
    it "is valid " do                      
      expect(project).to be_valid          
    end                                    
  end                                      
end                                                                

ここでは、事前に定義したprojectを呼び出し、そこからuserを呼び出しています。

テストは問題なく成功します。

context "when letのとき" do        
  it "is valid " do                
    expect(Project.count).to eq 1  
  end                              
end                                

では、次にProjectをカウントしてみます。

Failure/Error: expect(Project.count).to eq 1

  expected: 1
       got: 0

letでは、呼び出しを行わなければ実行されないので、カウントが0となってしまいます。

let!(:project) { Project.create(    
              name: "test_project", 
              owner: user           
)}                                  

次に、let!に変更します。

Users
  when letのとき
    is valid

Finished in 0.24935 seconds (files took 3.89 seconds to load)
1 example, 0 failures

テストに成功しました。

テスト前に、let!がbefore(:each)と同様に実行されていることがわかります。

まとめ

letとlet!の使い方についてまとめてみました。

beforeとlet!を使用して、リファクタリングすることは大切だと感じましたが

同様に多用しすぎると、テストコードが見辛くなる可能性も秘めているなと感じました。