6

我已经阅读了rspec 文档并搜索了许多其他地方,但我很难掌握 Rspecletlet!

我读过它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
4

2 回答 2

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

调用变量m1m2之前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调用中返回

于 2013-07-01T15:13:42.713 回答
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 测试都将导致失败。

于 2014-01-11T10:42:59.020 回答