ruby-on-rails RSpec:let和before块有什么区别?

n53p2ov0  于 2022-12-27  发布在  Ruby
关注(0)|答案(4)|浏览(193)

let和RSpec中的before块有什么区别?
什么时候使用每一种?
在下面的例子中,什么是好的方法(让或之前)?

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

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

我研究过这个
但是像上面这样定义let用于关联的东西好吗?

gev0vcfq

gev0vcfq1#

人们似乎已经解释了它们之间的一些基本差异,但却忽略了before(:all),也没有确切地解释为什么要使用它们。
我相信,绝大多数规范中都没有示例变量的位置,部分原因是this answer中提到的原因,所以我不会在这里提到它们。

字母块

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

你可以看到has_moustache在每种情况下都有不同的定义,但是没有必要重复subject的定义。需要注意的是,将使用当前上下文中定义的最后一个let块。这对于设置大多数规范使用的默认值很有帮助,如果需要,可以覆盖它。
例如,如果传递了top_hat设置为true但没有moustache的person模型,则检查calculate_awesome的返回值将为:

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

关于let块的另一个需要注意的事情是,如果你要搜索已经保存到数据库的东西(例如Library.find_awesome_people(search_criteria)),不应该使用let块,因为除非已经引用过let块,否则let块不会保存到数据库中。let!before块是这里应该使用的。
此外,* 永远不要**使用before来触发let块的执行,这就是let!的用途!

让!块

let!块按照定义的顺序执行(很像before块)。与before块的一个核心区别是,您获得了对该变量的显式引用,而不需要回退到示例变量。
let块一样,如果多个let!块被定义为相同的名称,则最近的块将在执行中使用。核心区别在于,如果这样使用,let!块将被执行多次,而let块将只执行最后一次。

在(:each)块之前

before(:each)是块之前的默认值,因此可以引用为before {},而不是每次都指定完整的before(:each) {}
我个人倾向于在一些核心情况下使用before块,我将在以下情况下使用before块:

  • 我在用嘲笑,打击或者替身
  • 有任何合理大小的设置(通常这是一个迹象,你的工厂特性没有设置正确)
  • 有许多变量我不需要直接引用,但设置时需要
  • 我正在用rails编写函数控制器测试,我想执行一个特定的测试请求(例如before { get :index }),尽管在很多情况下可以使用subject,但如果不需要引用,有时会感觉更明确。

如果您发现自己在为规范编写大型before块,请检查工厂,确保完全理解traits及其灵活性。

在(:所有)块之前

它们只执行一次,在当前上下文(及其子)的规范之前。如果编写正确,它们可以发挥很大的优势,因为在某些情况下,这可以减少执行和工作量。
一个例子(几乎不会影响执行时间)是模拟测试的ENV变量,您应该只需要做一次。

kwvwclae

kwvwclae2#

几乎总是,我更喜欢let .你链接的帖子指出let也更快.然而,有时,当许多命令必须执行时,我可以使用before(:each),因为当涉及许多命令时,它的语法更清晰.
在您的示例中,我肯定更喜欢使用let而不是before(:each),一般来说,当只完成一些变量初始化时,我倾向于使用let

w9apscun

w9apscun3#

还有一个很大的区别没有提到,用let定义的变量只有在你第一次调用它的时候才会示例化,所以before(:each)块会示例化所有变量,而let让你定义一些变量,你可以在多个测试中使用,它不会自动示例化它们。如果你希望所有的数据都被预先加载,你的测试可能会反过来咬你自己。在某些情况下,你甚至可能想定义一些let变量,然后使用before(:each)块调用每个let示例,只是为了确保数据在开始时可用。

tkclm6bt

tkclm6bt4#

看起来你正在使用Machinist。注意,你可能会看到let的make!inside(非bang版本)在全局fixture事务之外发生一些问题(如果你也在使用事务fixture的话),从而破坏了其他测试的数据。

相关问题