首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >难以区分Rspec的“let”和“let!”

难以区分Rspec的“let”和“let!”
EN

Stack Overflow用户
提问于 2013-07-01 23:06:29
回答 2查看 3.5K关注 0票数 6

我已经阅读了rspec docs,并搜索了许多其他地方,但是我很难理解Rspec的letlet!之间的区别

我读到过let只有在需要时才会被初始化,并且它的值只在每个示例中被缓存。我还读到过let!强制变量立即存在,并强制调用每个示例。我猜因为我是新手,所以我很难理解这与下面的例子有什么关系。为什么需要用let!设置:m1来断言页面上存在m1.content,而可以用let设置:user来断言页面包含text: user.name

代码语言:javascript
复制
  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
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-07-01 23:13:43

因为之前的代码块调用的是visit user_path(user),所以用户值在那里被初始化,RSpec将访问该页面。如果:m1 :m2没有使用let!,那么访问将不会产生任何内容制作

代码语言:javascript
复制
it { should have_content(m1.content) }
it { should have_content(m2.content) }

失败,因为它希望在用户访问页面之前创建微帖子。let!允许在调用之前代码块之前创建微帖子,并且当测试访问页面时,微帖子应该已经创建。

另一种编写相同测试并使其通过的方法是执行以下操作:

代码语言:javascript
复制
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

visit user_path(user)之前调用变量m1m2会导致在访问页面之前对它们进行初始化,并导致测试通过。

UPDATE这个小例子更有意义:

在本例中,我们调用get_all_posts,它只返回一个帖子数组。注意,我们在断言之前和it块执行之前调用了该方法。因为post在断言执行之前不会被调用。

代码语言:javascript
复制
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,并在Post列表中返回post

同样,另一种做同样事情的方法是在调用方法之前先在之前的块中调用变量名

代码语言:javascript
复制
before do
  post
  @response = get_all_posts
end

因为这将确保在调用方法本身之前调用let(:post)块,从而创建Post,以便在Post.all调用中返回它

票数 12
EN

Stack Overflow用户

发布于 2014-01-11 18:42:59

区分的关键是rspec如何执行这些步骤。

再看一遍代码:

代码语言:javascript
复制
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测试都将导致失败。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17407733

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档