我已阅读rspec docs并搜索了其他一些地方,但我很难掌握Rspec的let
和let!
我已经读过let
在需要之前没有被初始化,并且它的值仅在每个示例中被缓存。我还读到let!
迫使变量立即存在,并强制调用每个示例。我想因为我是新手,所以我很难看到这与下面的例子有什么关系。为什么:m1
需要设置为let!
以断言m1.content
页面上存在,:user
可以设置为let
以断言该页面包含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('user@example.com') }
it { should have_selector('title', text: user.name) }
it { should have_success_message('Welcome') }
it { should have_link('Sign out') }
end
答案 0 :(得分:12)
因为before块正在调用visit user_path(user)
,所以用户值在那里初始化,RSpec将访问该页面。如果:m1
:m2
未使用let!
,则访问不会产生任何内容
it { should have_content(m1.content) }
it { should have_content(m2.content) }
失败,因为它希望在用户访问页面之前创建微博。 let!
允许在调用before块之前创建微博,当测试访问页面时,微博应该已经创建。
编写相同测试并让它们通过的另一种方法是执行以下操作:
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
在m1
之前调用变量m2
和visit user_path(user)
会导致在访问页面之前初始化它们并导致测试通过。
<强>更新强> 这个小例子会更有意义:
在这个例子中,我们调用get_all_posts,它只返回一个帖子数组。请注意,我们在断言之前和it
块执行之前调用该方法。由于在执行断言之前不会调用post。
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!
只要RSpec看到方法(before
块之前)就会创建帖子,帖子将在Post
同样,另一种方法是在我们调用方法之前调用before块中的变量名
before do
post
@response = get_all_posts
end
因为这将确保在调用方法本身之前调用let(:post)
块,从而创建Post
以便在Post.all
调用中返回
答案 1 :(得分:0)
区分的关键是rspec如何执行这些步骤。
再次查看代码:
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) }
如果我们使用let而不是let !,则此时不会创建m1和m2。然后Rspec进行访问并加载页面,但显然页面上没有m1或m2。
所以现在如果我们调用m1和m2,它们将在内存中创建。但是已经太晚了,因为除非我们故意这样做,否则页面不会被再次加载。因此,页面上的任何UI测试都将导致失败。