ChatGPT解决这个技术问题 Extra ChatGPT

RSpec:let 和 before 块有什么区别?

RSpec 中的 letbefore 块有什么区别?

以及何时使用每个?

在下面的示例中,什么是好的方法(让或之前)?

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

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

我研究了这个stackoverflow post

但是为上面的关联内容定义 let 是否很好?

基本上,不喜欢实例变量的人会使用“let”。附带说明一下,您应该考虑使用 FactoryGirl 或类似工具。

J
Jay

人们似乎已经解释了它们不同的一些基本方式,但忽略了 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 但没有胡子的 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!before 块。

此外,永远永远使用 before 来触发 let 块的执行,这就是 let! 的用途!

让!块

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

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

before(:each) 块

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

在一些核心情况下使用 before 块是我个人的偏好。如果出现以下情况,我将使用 before 块:

我正在使用嘲笑、存根或双打

有任何合理大小的设置(通常这是您的工厂特征设置不正确的标志)

有许多变量我不需要直接引用,但在设置时是必需的

我在 Rails 中编写功能控制器测试,我想执行一个特定的测试请求(即在 { get :index } 之前)。即使您可以在很多情况下为此使用主题,但如果您不需要参考,有时会感觉更明确。

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

before(:all) 块

这些只会在当前上下文(及其子项)中的规范之前执行一次。如果编写正确,这些可以发挥很大的优势,因为在某些情况下,这可以减少执行和工作量。

一个例子(它几乎不会影响执行时间)是模拟一个 ENV 变量进行测试,你应该只需要做一次。

希望有帮助:)


对于 minitest 用户,但我没有注意到此问答的 RSpec 标签,Minitest 中不存在 before(:all) 选项。以下是评论中的一些解决方法:github.com/seattlerb/minitest/issues/61
引用的文章是一个死链接:\
谢谢@DylanPierce。我找不到那篇文章的任何副本,所以我引用了一个 SO answer 来解决这个问题:)
谢谢:) 非常感谢
its 不再在 rspec-core 中。一种更现代、更惯用的方式是 it { is_expected.to be_awesome }
J
JJD

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

在您的示例中,我肯定更喜欢使用 let 而不是 before(:each)。一般来说,当只是完成一些变量初始化时,我倾向于使用 let


m
mikeweber

尚未提及的一个很大区别是,使用 let 定义的变量在您第一次调用它之前不会被实例化。因此,虽然 before(:each) 块会实例化所有变量,但 let 让您定义一些您可能会在多个测试中使用的变量,但它不会自动实例化它们。在不知道这一点的情况下,如果您期望预先加载所有数据,您的测试可能会重新陷入困境。在某些情况下,您甚至可能想要定义多个 let 变量,然后使用 before(:each) 块来调用每个 let 实例,以确保数据可以开始使用。


您可以使用 let! 定义在每个示例之前调用的方法。请参阅RSpec docs
T
Tim Connor

看起来您正在使用机械师。请注意,您可能会看到 make 的一些问题! let 内部(非爆炸版本)发生在全局夹具事务之外(如果您也使用事务夹具),因此破坏了其他测试的数据。