"let" 在 RSpec 测试中真正节省了多少时间?
How much time does "let" really save in RSpec tests?
我发现在我的代码中设置变量比使用 let
更容易。 let
很挑剔,总是告诉我错误的使用方式。
当我在规范中使用简单的变量声明时,例如
tx_good = makeTransaction1()
,一切正常。
但是当我像这样使用let
时
let(: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
的急切(即不懒惰)版本
let
已记忆
块内定义的任何内容都只会发生一次(在上下文范围内):
context do
let(:random_number) { rand }
specify do
expect(random_number).to eq(random_number) # will always pass
end
end
如果你不想要这个功能,定义一个方法:
context do
def random_number
rand
end
specify do
expect(random_number).to eq(random_number) # sometimes pass, mostly fail
end
end
let
在较低级别的上下文中覆盖来自较高级别的 let
定义:
context do
let(:x) { 1 }
specify { expect(x).to eq(1) # pass
context 'with different x' do
let(:x) { 2 }
specify { expect(x).to eq(2) # pass
end
context do
specify { expect(x).to eq(1) # pass
end
end
^ 这允许您以某种方式编写规范,在上下文中仅提及相关的 "pieces" 设置,例如:
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
# writing
subject { foo(x) }
# is almost the same as writing
let(:subject) { foo(x) }
subject
是 RSpec 中的一个保留概念,它是一个 "thing you test" 所以你可以用 `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
关于您的错误...
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.
你正在做类似
的事情
before do
let(:x) { ... }
end
干脆不要做了,你在describe
和context
里面定义了let
,但是你可以在[=里面使用它们(不定义它们,使用定义的东西) 37=] 和 specify
:
let(:name) { 'Frank' }
before do
User.create name: name
end
specify do
expect(User.where(name: name).count).to eq(1)
end
我发现在我的代码中设置变量比使用 let
更容易。 let
很挑剔,总是告诉我错误的使用方式。
当我在规范中使用简单的变量声明时,例如
tx_good = makeTransaction1()
,一切正常。
但是当我像这样使用let
时
let(: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
let
已记忆
块内定义的任何内容都只会发生一次(在上下文范围内):
context do
let(:random_number) { rand }
specify do
expect(random_number).to eq(random_number) # will always pass
end
end
如果你不想要这个功能,定义一个方法:
context do
def random_number
rand
end
specify do
expect(random_number).to eq(random_number) # sometimes pass, mostly fail
end
end
let
在较低级别的上下文中覆盖来自较高级别的 let
定义:
context do
let(:x) { 1 }
specify { expect(x).to eq(1) # pass
context 'with different x' do
let(:x) { 2 }
specify { expect(x).to eq(2) # pass
end
context do
specify { expect(x).to eq(1) # pass
end
end
^ 这允许您以某种方式编写规范,在上下文中仅提及相关的 "pieces" 设置,例如:
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
# writing
subject { foo(x) }
# is almost the same as writing
let(:subject) { foo(x) }
subject
是 RSpec 中的一个保留概念,它是一个 "thing you test" 所以你可以用 `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
关于您的错误...
let
andsubject
declarations are not intended to be called in abefore(:context)
hook, as they exist to define state that is reset between each example, whilebefore(:context)
exists to
define state that is shared across examples in an example group.
你正在做类似
的事情before do
let(:x) { ... }
end
干脆不要做了,你在describe
和context
里面定义了let
,但是你可以在[=里面使用它们(不定义它们,使用定义的东西) 37=] 和 specify
:
let(:name) { 'Frank' }
before do
User.create name: name
end
specify do
expect(User.where(name: name).count).to eq(1)
end