难以区分Rspec的'let'与'let!'

时间:2013-07-01 15:06:29

标签: rspec let

我已阅读rspec docs并搜索了其他一些地方,但我很难掌握Rspec的letlet!

之间的区别

我已经读过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

2 个答案:

答案 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之前调用变量m2visit 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测试都将导致失败。