2013-07-01 15 views
6

私はrspec docsを読んでいると、他の場所の数を検索しましたが、私は、私はそれが必要になるまでletが初期化されていないことを読み、そのことをしましたRSPECのletlet!Rspecの 'let'と 'let'を区別するのに問題があります。

の違いを把握困難な時間を過ごしています値は例ごとにキャッシュされます。また、let!が変数を直接的に強制的に存在させ、各例の呼び出しを強制することも読んでいます。私は新しいので、次の例とどう関係するか見ているのが難しいですね。 :m1let!と設定して、ページにm1.contentが存在するのはなぜですか?:userletと設定すると、ページにはtext: user.nameが含まれていると主張できますか?

subject { page } 

    describe "profile page" do 
    let(:user) { FactoryGirl.create(:user) } 
    let!(:m1) { FactoryGirl.create(:micropost, user: user, content: "Foo") } 
    let!(:m2) { FactoryGirl.create(:micropost, user: user, content: "Bar") } 

    before { visit user_path(user) } 

    it { should have_selector('h1', text: user.name) } 
    it { should have_selector('title', text: user.name) } 

    describe "microposts" do 
     it { should have_content(m1.content) } 
     it { should have_content(m2.content) } 
     it { should have_content(user.microposts.count) } 
    end 
    end 

    describe "after saving the user" do 
    before { click_button submit } 
    let(:user) { User.find_by_email('[email protected]') } 

    it { should have_selector('title', text: user.name) } 
    it { should have_success_message('Welcome') } 
    it { should have_link('Sign out') } 
    end 

答えて

12

前のブロックは、ユーザー値が初期化されるとRSpecのは、そのページを訪問するvisit user_path(user)を呼び出しているので。 :m1:m2let!を使用していなかった場合、その後の訪問は、ユーザーがページを訪問する前に、マイクロポストが作成されることを想定しているため

it { should have_content(m1.content) } 
it { should have_content(m2.content) } 

が失敗することはコンテンツが得られないだろう。 let!は、beforeブロックが呼び出される前にマイクロポストを作成できるようにします。また、テストがページを訪問すると、マイクロポストはすでに作成されています。同じテストを記述し、それらを渡す持っている

もう一つの方法は、次のことをやっている:

visit user_path(user)ページを訪問しにテストを引き起こしている前に初期化するためにそれらを原因とする前に、変数 m1m2を呼び出す
describe "profile page" do 
    let(:user) { FactoryGirl.create(:user) } 
    let(:m1) { FactoryGirl.create(:micropost, user: user, content: "Foo") } 
    let(:m2) { FactoryGirl.create(:micropost, user: user, content: "Bar") } 

    before do 
    m1 
    m2 
    visit user_path(user) 
    end 

パス。

UPDATE この小さな例では、より理にかなって:

この例では、我々はちょうどポストの配列を返すget_all_postsを呼び出しています。アサーションの前で、そしてitブロックが実行される前にメソッドを呼び出すことに注目してください。アサーションが実行されるまで、postは呼び出されません。ポストは、すぐにRSpecのは(beforeブロックの前に)メソッドを見ているように作成さになるだろうとポストが再びPost

のリストで返さになるだろう、実行する別の方法let!を使用して

def get_all_posts 
    Post.all 
end 

let(:post) { create(:post) } 

before { @response = get_all_posts } 

it 'gets all posts' do 
    @response.should include(post) 
end 

我々はこの方法自体はカロリーである前let(:post)ブロックが呼び出されることを保証しますと

before do 
    post 
    @response = get_all_posts 
end 

メソッドを呼び出す前に、同じブロックの前に、変数の名前でを呼び出すことであろうPostが作成され、Post.allコールで返されるようになりました。

+0

これは理にかなっています。したがって、m1がまだ初期化されていないため、オブジェクト(m1)にメッセージ(コンテンツ)を送信するだけでは十分ではありません。 – KMcA

+0

これは、テストが失敗する理由ではありません。テストが失敗する理由は、マイクロポストを作成する前に既に「ページを訪問した」ため、ページに内容がないためです。あなたがまだ疑問を持っているなら、私の答えをもう少し明確にするように更新することができますか? –

+0

もう一度私の質問(「ユーザーを保存した後」)をもう一度見て、2番目のテストで「user.name」を確認する方法はありますか? – KMcA

0

rspecがステップを実行する方法だけです。再びコードで

ルック:私たちが代わりにletの聞かせて使用する場合

let(:user) { FactoryGirl.create(:user) } 
let!(:m1) { FactoryGirl.create(:micropost, user: user, content: "Foo") } 
let!(:m2) { FactoryGirl.create(:micropost, user: user, content: "Bar") } 

before { visit user_path(user) } 

!、M1とM2は、現時点では作成されません。 Rspecは訪問を行い、ページが読み込まれますが、明らかにページにm1もm2もありません。

ここでm1とm2を呼び出すと、それらはメモリに作成されます。しかし、私たちが意図的にそうしない限り、ページが再びロードされないので、すでに遅すぎます。したがって、ページ上のUIテストはすべて失敗に終わります。