Since visit user_path(user) is called before the block, the user value is initialized there, and RSpec will visit this page. If :m1 :m2 did not use let! then the visit will not lead to the creation of content
it { should have_content(m1.content) } it { should have_content(m2.content) }
crash because it expects microposts to be generated before the user visits the page. let! allows you to create microcells before the called block is called, and when tests visit the page, microcells must already be created.
Another way to write the same tests and skip them, does the following:
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
calling the m1 and m2 variables before visit user_path(user) causes them to be initialized before the page is visited and causes the tests to pass.
UPDATE This small example makes sense:
In this example, we call get_all_posts, which returns an array of messages. Note that we call the method before the statement and before the it block is executed. Because the message is not called until the statement is executed.
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
using let! the post will be created as soon as RSpec sees the method (before the before block), and the message will return to the Post list
Again, another way to do the same would be to call the variable name in front of the block before we call the method
before do post @response = get_all_posts end
since this ensures that the let(:post) block is called before the method that creates Post is called, so that it returns to the Post.all call