RSpec:let和a block之间有什么区别?

kri*_*sna 87 unit-testing rspec ruby-on-rails

RSpec letbefore块之间的区别是什么?

什么时候使用?

下面的例子中有什么好方法(让或之前)?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end
Run Code Online (Sandbox Code Playgroud)

我研究过这个stackoverflow帖子

但是为上面的关联内容定义let是否合适?

Jay*_*Jay 137

人们似乎已经解释了一些在他们不同的基本途径,但冷落before(:all)和不完全解释为什么他们应该被使用.

这是我的信念,实例变量在绝大多数规格的没有使用的地方,部分原因是由于提到的原因这个答案,所以我不会提到他们作为一个不错的选择.

让块

let块中的代码仅在引用时执行,延迟加载这意味着这些块的排序无关紧要.这为您提供了大量的功能,可以通过您的规格减少重复设置.

一个(非常小而且做作)的例子是:

let(:person)     { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }

context 'with a moustache' do
  let(:has_moustache) { true } 
  its(:awesome?)      { should be_true }
end

context 'without a moustache' do
  let(:has_moustache) { false } 
  its(:awesome?)      { should be_false }
end
Run Code Online (Sandbox Code Playgroud)

您可以看到has_moustache在每种情况下都有不同的定义,但不需要重复subject定义.需要注意的一点是,let将使用当前上下文中定义的最后一个块.这适用于设置大多数规范使用的默认值,如果需要可以覆盖.

例如,检查calculate_awesomeif 的返回值是否传递了一个设置为true 的person模型top_hat,但没有胡子:

context 'without a moustache but with a top hat' do
  let(:has_moustache) { false } 
  let(:person)        { build(:person, top_hat: true) }
  its(:awesome?)      { should be_true }
end
Run Code Online (Sandbox Code Playgroud)

关于let块的另一个注意事项是,如果你正在搜索保存到数据库中的东西(即Library.find_awesome_people(search_criteria)),它们不应该被使用,因为它们不会被保存到数据库,除非它们已被引用.let!before块是这里应该使用的.

此外,从来没有使用before触发的执行let块,这是let!用于制造!

让!块

let!块按照它们的定义顺序执行(很像前一个块).与块之前的一个核心差异是,您获得了对此变量的显式引用,而不是需要回退到实例变量.

let块一样,如果let!使用相同的名称定义多个块,则最新的块将用于执行.核心区别在于let!如果像这样使用块将被执行多次,而let块将仅执行最后一次.

之前(:每个)阻止

before(:each)是块之前的默认值,因此可以引用,before {}而不是before(:each) {}每次都指定完整.

我个人倾向于before在几种核心情况下使用块.如果出现以下情况,我将使用块

  • 我正在使用嘲弄,抄写或双打
  • 有任何合理大小的设置(通常这是一个标志,你的工厂特征没有正确设置)
  • 有许多变量我不需要直接引用,但是需要进行设置
  • 我在rails中编写功能控制器测试,我想执行一个特定的测试请求(即before { get :index }).即使您可以subject在很多情况下使用它,但如果您不需要引用,有时会感觉更明确.

如果您发现自己before为规范编写大块,请检查您的工厂并确保您完全了解特征及其灵活性.

之前(:所有)块

这些只在当前上下文(及其子代)中的规范之前执行过一次.如果正确编写,这些可以非常有用,因为在某些情况下这可以减少执行和工作量.

一个例子(根本不会影响执行时间)是为测试模拟一个ENV变量,你应该只需要做一次.

希望有帮助:)


Spy*_*ros 25

几乎总是,我更喜欢let.您链接的帖子指定let也更快.但是,有时,当必须执行许多命令时,我可以使用,before(:each)因为当涉及许多命令时,它的语法更清晰.

在你的例子中,我肯定更愿意使用let而不是before(:each).一般来说,当只进行一些变量初始化时,我倾向于使用let.


mik*_*ber 15

没有提到的一个很大的区别是,定义的变量let在第一次调用之前不会被实例化.因此,虽然before(:each)块将实例化所有变量,但是let您可以定义多个可能在多个测试中使用的变量,但它不会自动实例化它们.如果您希望预先加载所有数据,那么在不知道这一点的情况下,您的测试可能会回过头来咬自己.在某些情况下,您甚至可能希望定义许多let变量,然后使用before(:each)块来调用每个let实例,以确保数据可用.

  • 您可以使用`let!`来定义在每个示例之前调用的方法.请参阅[RSpec docs](https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let). (20认同)