“需要多少时间?”;让我们;在RSpec测试中真的节省了吗?

“需要多少时间?”;让我们;在RSpec测试中真的节省了吗?,rspec,performance-testing,rspec-rails,let,Rspec,Performance Testing,Rspec Rails,Let,我发现在代码中设置变量比使用let容易得多let很挑剔,总是告诉我错误使用它的方式 当我在我的规范中使用一个简单的变量声明时,比如 tx\u good=makeTransaction1(),一切正常 但是当我使用时,就让像这样 让(:tx_good){makeTransaction1()}我总是会遇到这样的错误,告诉我它不能在这里或那里运行 `let` and `subject` declarations are not intended to be called in a `befo

我发现在代码中设置变量比使用
let
容易得多
let
很挑剔,总是告诉我错误使用它的方式

当我在我的规范中使用一个简单的变量声明时,比如

tx\u good=makeTransaction1()
,一切正常

但是当我使用
时,就让
像这样

让(:tx_good){makeTransaction1()}
我总是会遇到这样的错误,告诉我它不能在这里或那里运行

  `let` and `subject` declarations are not intended to be called
   in a `before(:context)` hook, as they exist to define state that
   is reset between each example, while `before(:context)` exists to
   define state that is shared across examples in an example group.
考虑到使用
let
是多么的挑剔,我不得不怀疑它是否值得我为此付出额外的努力和谨慎。有人知道使用let与仅仅预先分配变量相比,真正节省了多少处理时间吗


我想遵循良好的测试协议,所以我希望有人能说服我,为什么我应该像(似乎)其他人一样使用
let

你用错了这些东西,我理解你的沮丧。因此,让我给你一本在RSpec中使用
let
s的简明手册

使用
let
的主要价值并非来自节省的处理能力。它是更广泛的RSpec理念的组成部分。我会尽力解释,希望你能更容易进步

let
是懒惰的 当且仅当在等级库中实际使用时,才会调用块内定义的任何内容:

context do
  let(:foo) { sleep(10000) } # will not happen
  specify { expect(1).to eq(1) }
end 

context do 
  specify do 
     foo = sleep(10000) # you'll wait
     expect(1).to eq(1)
  end
end
使用
let
,它是渴望(即不懒惰)版本的
let

被记忆 在块内定义的内容只会发生一次(在上下文范围内):

如果不需要此功能,请定义一种方法:

context do
  def random_number
    rand
  end
  specify do
    expect(random_number).to eq(random_number) # sometimes pass, mostly fail
  end
end
let
在较低级别的上下文中覆盖较高级别的定义: ^这允许您以一种只在上下文中提及设置的相关“部分”的方式来编写规范,例如:

context do 
   let(:x) { 1 }
   let(:y) { 1 }
   let(:z) { 1 }
   specify { expect(foo(x, y, z)).to eq(3) }

   context 'when z is nil'
     let(:z) { nil }
     specify { expect(foo(x, y, z)).to raise_error) } # foo doesn't work with z = nil
   end

   context 'when x is nil'
     let(:x) { nil }
     specify { expect(foo(x, y, z)).to eq(15) } 
   end
end
奖励:
subject
是一个神奇的
let
subject
在RSpec中是一个保留的概念,它是一个“你要测试的东西”,所以你可以用`foo(x,y,z)这样写这个例子:

context do 
   let(:x) { 1 }
   let(:y) { 1 }
   let(:z) { 1 }
   subject { foo(x, y, z) }
   specify { expect(subject).to eq(3) }

   context 'when z is nil'
     let(:z) { nil }
     specify { expect(subject).to raise_error) } # foo doesn't work with z = nil
   end

   context 'when x is nil'
     let(:x) { nil }
     specify { expect(foo(subject)).to eq(15) } 
   end
end
关于你的错误。。。
主题
声明不被调用 (:context)
之前的
钩子,因为它们的存在定义了
在每个示例之间重置,而(:context)
之前的
存在于
定义示例组中跨示例共享的状态

你在做类似的事情

before do
  let(:x) { ... }
end
不要这样做,你可以在
中定义
描述
上下文
,但你可以在
中使用它们(不定义它们,使用定义的内容),然后在
中指定

let(:name) { 'Frank' }
before do
  User.create name: name
end

specify do
   expect(User.where(name: name).count).to eq(1)
end

让我们给出一种共享代码和依赖项的方法,这些代码和依赖项是惰性的,并且是线程安全的。从你的例子来看,你没有正确地使用它们,所以在你放弃它们之前,我会先试用一下。第二@Anthony我同样对
let
感到失望,很久以前,这有点让人困惑,因为你似乎在测试之外声明变量。您可以将
let
放在
description
context
块中,但不能放在
it
块之前的
中。基本上,这会创建一个可重用的变量,为每个测试创建一个新的变量,这有助于防止测试之间的数据库工件,并具有延迟加载的额外好处,延迟加载只在调用变量后创建该变量。这可能会造成表面上的“挑剔”行为,但实际上是在做它应该做的事。:)经过深思熟虑的解释应该被接受,因此
let
对于设置不同的场景进行测试非常有用,而不会以可能破坏其他测试的方式更改共享变量的状态。有点像把一个变量传递给一个小函数,然后创建它自己的作用域。它似乎也减少了对新变量名的需求,因为我们可以在不同的上下文中重用相同的符号。还请注意,单行语法允许您将“expect(subject)”替换为“is_expected”或替换为“expect(subject)”。to“to”替换为“should”,例如subject{3};它{应为等式(3)};它{被期望为等式(3)}
context do 
   let(:x) { 1 }
   let(:y) { 1 }
   let(:z) { 1 }
   subject { foo(x, y, z) }
   specify { expect(subject).to eq(3) }

   context 'when z is nil'
     let(:z) { nil }
     specify { expect(subject).to raise_error) } # foo doesn't work with z = nil
   end

   context 'when x is nil'
     let(:x) { nil }
     specify { expect(foo(subject)).to eq(15) } 
   end
end
before do
  let(:x) { ... }
end
let(:name) { 'Frank' }
before do
  User.create name: name
end

specify do
   expect(User.where(name: name).count).to eq(1)
end