Ruby 轨道-RSpec-之间的差异;让我们;及;让我来&引用;

Ruby 轨道-RSpec-之间的差异;让我们;及;让我来&引用;,ruby,rspec,rspec2,Ruby,Rspec,Rspec2,我已经读到了关于差异的说法,但有些事情仍然令人困惑。所有其他来源,包括“RSpec手册”只解释了“let”和“Rails 3-Way”,与手册一样令人困惑 我知道“let”只有在被调用时才被计算,并且在一个范围内保持相同的值。因此,在本例的第一个示例中,第一个测试作为“let”仅调用一次而通过,第二个测试作为它添加到第一个测试的值(第一个测试中评估了一次,值为1)而通过是有意义的 接下来,由于“let!”在定义时进行计算,在调用时再次进行计算,那么测试是否应该失败为“count.should e

我已经读到了关于差异的说法,但有些事情仍然令人困惑。所有其他来源,包括“RSpec手册”只解释了“let”和“Rails 3-Way”,与手册一样令人困惑

我知道“let”只有在被调用时才被计算,并且在一个范围内保持相同的值。因此,在本例的第一个示例中,第一个测试作为“let”仅调用一次而通过,第二个测试作为它添加到第一个测试的值(第一个测试中评估了一次,值为1)而通过是有意义的

接下来,由于“let!”在定义时进行计算,在调用时再次进行计算,那么测试是否应该失败为“count.should eq(1)”而不是“count.should eq(2)”


任何帮助都将不胜感激。

您可以阅读更多关于这方面的内容,但基本上是这样<代码>(:let)是延迟计算的,如果您不调用它,它将永远不会被实例化,而
(:let!)
是在每次方法调用之前被强制计算的。

它在定义时不会被调用,而是在每个示例之前被调用(然后它被记住,并且不会被示例再次调用)。这样,count的值将为1

无论如何,如果您有另一个示例,则再次调用before钩子-以下所有测试都通过:

$count = 0
describe "let!" do
  invocation_order = []

  let!(:count) do
    invocation_order << :let!
    $count += 1
  end

  it "calls the helper method in a before hook" do
    invocation_order << :example
    invocation_order.should == [:let!, :example]
    count.should eq(1)
  end

  it "calls the helper method again" do
    count.should eq(2)
  end
end
$count=0
描述“让我做!”
调用顺序=[]
让我来!(:count)做什么

调用顺序我也被
let
let搞糊涂了,因此我从中获取了文档代码并使用它:


希望有帮助

我也认为这令人困惑,但我认为Rails 3-Way的例子很好。
let类似于before块中的实例变量,而let!立即记录

从轨道3路

describe BlogPost do
  let(:blog_post) { BlogPost.create :title => 'Hello' }
  let!(:comment) { blog_post.comments.create :text => 'first post' }

  describe "#comment" do
    before do
     blog_post.comment("finally got a first post")
    end

    it "adds the comment" do
      blog_post.comments.count.should == 2
    end
  end
end
“因为注释块永远不会在第一次执行 断言如果使用let定义,则只有一条注释 即使实现可能正在运行,也已添加到此规范中。 通过使用let!我们确保创建了初始注释,并且规范 现在将过去。”


我理解了
let
let之间的区别使用一个非常简单的示例。让我先读一下doc语句,然后亲自展示输出

医生说:-

let
是惰性求值的:直到第一次才求值 调用它定义的方法


我通过下面的例子理解了区别:-

$count = 0
describe "let" do
  let(:count) { $count += 1 }

  it "returns 1" do
    expect($count).to eq(1)
  end
end
让我们现在运行它:-

arup@linux-wzza:~/Ruby> rspec spec/test_spec.rb
F

Failures:

  1) let is not cached across examples
     Failure/Error: expect($count).to eq(1)

       expected: 1
            got: 0

       (compared using ==)
     # ./spec/test_spec.rb:8:in `block (2 levels) in <top (required)>'

Finished in 0.00138 seconds (files took 0.13618 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/test_spec.rb:7 # let is not cached across examples
arup@linux-wzza:~/Ruby>
让我们运行以下代码:-

arup@linux-wzza:~/Ruby> rspec spec/test_spec.rb
.

Finished in 0.00145 seconds (files took 0.13458 seconds to load)
1 example, 0 failures
请参阅,现在
$count
返回
1
,因此测试通过。它发生在我使用
let时,它在示例运行之前运行,尽管我们没有在示例中调用
count


这就是
let
let彼此不同。

这里有一种方法可以让您的规格保持可预测性

你应该经常使用
let
。您不应该使用
let除非您有意要跨示例缓存该值。这就是为什么:

describe '#method' do
  # this user persists in the db across all sub contexts
  let!(:user) { create :user }

  context 'scenario 1' do
    context 'sub scenario' do
      # ...
      # 1000 lines long
      # ...
    end

    context 'sub scenario' do
      # you need to test user with a certain trait
      # and you forgot someone else (or yourself) already has a user created
      # with `let!` all the way on the top
      let(:user) { create :user, :trait }

      it 'fails even though you think it should pass' do
        # this might not be the best example but I found this pattern
        # pretty common in different code bases
        # And your spec failed, and you scratch your head until you realize
        # there are more users in the db than you like
        # and you are just testing against a wrong user
        expect(User.first.trait).to eq xxx
      end
    end
  end
end

谢谢你,贾斯汀。那是我链接到的我不理解的“手册”。我的问题更多地集中在为什么链接中的“第二个规范”以“count.should eq(1)”而不是我预期的“count.should eq(2)”。如果在方法调用之前进行评估,并且在调用“count.should”时再次进行评估,那么第二个示例不应该等于2而不是1吗?halle flippin lujah,最后给出了一个解释,该解释没有涉及到记忆或是什么。简单而中肯,一切都应该如此。同意,这就是为什么第一个规范是有意义的。当然,第二个规范将在示例之前进行评估(使其等于1),然后在调用“count.should”(使其等于2)时再次进行评估?不,它已被调用和记忆,因此$count不会再次增加。无论如何,如果您有另一个示例,那么将再次调用before钩子。看到我编辑过的答案,我添加了一些代码来澄清。这意味着如果你使用“let”和一个spec(一个“it”)不doa“count.should”(或类似),那么增量就不会发生了?如果是这样,那么“let”就不应该被视为“before”,因为before默认意味着“let!”的功能。还是我又错过了什么?是记忆,不是记忆。从Obie Fernandez的书中可以看出,“memonized意味着与let相关联的代码块执行一次并存储起来以备将来调用,从而提高性能。”复制doc示例代码首先是问题的一部分。因此这里没有太大帮助。请记住,永远不要在块内有一个let块,这就是let块!这是为你而做的。有关更多信息,请查看此页面:
describe '#method' do
  # this user persists in the db across all sub contexts
  let!(:user) { create :user }

  context 'scenario 1' do
    context 'sub scenario' do
      # ...
      # 1000 lines long
      # ...
    end

    context 'sub scenario' do
      # you need to test user with a certain trait
      # and you forgot someone else (or yourself) already has a user created
      # with `let!` all the way on the top
      let(:user) { create :user, :trait }

      it 'fails even though you think it should pass' do
        # this might not be the best example but I found this pattern
        # pretty common in different code bases
        # And your spec failed, and you scratch your head until you realize
        # there are more users in the db than you like
        # and you are just testing against a wrong user
        expect(User.first.trait).to eq xxx
      end
    end
  end
end