我倾向于使用before块来设置实例变量。然后在示例中使用这些变量。我最近偶然发现let()。根据RSpec文档,它被用来

... 定义一个记忆helper方法。该值将在同一个示例中的多个调用之间缓存,但不会在多个示例之间缓存。

这与在块之前使用实例变量有什么不同?以及什么时候应该使用let() vs before()?


当前回答

使用实例变量和let()之间的区别在于let()是延迟求值的。这意味着let()直到它定义的方法第一次运行时才会被求值。

before和let之间的区别在于,let()提供了一种以“级联”风格定义一组变量的好方法。这样做可以简化代码,使规范看起来更好一些。

其他回答

“before”默认为before(:each)。参考Rspec Book,版权2010年,第228页。

before(scope = :each, options={}, &block)

我使用before(:each)为每个示例组播种一些数据,而不必调用let方法在“it”块中创建数据。在这种情况下,“it”块中的代码更少。

如果在某些例子中需要某些数据,而在其他例子中不需要,我会使用let。

before和let都可以很好地晾干“it”块。

为了避免混淆,“let”和前面的(:all)不一样。“Let”为每个示例(“it”)重新计算它的方法和值,但在同一个示例中跨多个调用缓存值。你可以在这里阅读更多信息:https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let

我使用let来测试我的API规范中的HTTP 404响应,使用上下文。

为了创建资源,我使用let!但是为了存储资源标识符,我使用let。看看它是什么样子的:

let!(:country)   { create(:country) }
let(:country_id) { country.id }
before           { get "api/countries/#{country_id}" }

it 'responds with HTTP 200' { should respond_with(200) }

context 'when the country does not exist' do
  let(:country_id) { -1 }
  it 'responds with HTTP 404' { should respond_with(404) }
end

这样可以保持规范的干净和可读性。

一般来说,let()是一种更好的语法,它节省了您在所有地方输入@name符号的时间。但是,买者自负!我发现let()也会引入一些微妙的错误(或者至少让人头疼),因为在你尝试使用它之前,这个变量并不真正存在……提示符号:如果在let()之后添加一个put以查看变量是否正确,则允许规范通过,但如果没有put,则规范失败——您已经发现了这个微妙之处。

我还发现let()似乎在所有情况下都不缓存!我把它写在我的博客上:http://technicaldebt.com/?p=1242

也许只有我这样?

重要的是要记住,let是惰性求值的,不要在其中放置副作用方法,否则您将无法轻松地从let更改到(:each)之前。 你可以用let!而不是let,以便在每个场景之前对其进行评估。

反对的声音在这里:经过5年的rspec,我不太喜欢让。

1. 惰性计算常常使测试设置令人困惑

当在setup中声明的一些东西实际上不会影响状态,而其他东西则会影响状态时,很难对setup进行推理。

最终,出于无奈,有人才把let改成了let!(没有懒惰的评估也是一样的),以便让他们的规范工作。如果这对他们来说是可行的,一个新的习惯就会诞生:当一个新的规范被添加到一个旧的套件中,但它不起作用时,作者尝试的第一件事就是在随机let调用中添加bangs。

很快,所有的性能优势都消失了。

2. 对于非rspec用户来说,特殊语法是不常见的

我宁愿把Ruby教给我的团队,而不是rspec的技巧。实例变量或方法调用在这个项目和其他项目中都很有用,让语法只在rspec中有用。

3.这些“好处”让我们很容易忽略好的设计变更

Let()适用于不希望反复创建的昂贵依赖项。 它还可以很好地与subject配合使用,允许您停止重复调用多参数方法

重复多次的昂贵依赖项和具有大签名的方法都是我们可以改进代码的地方:

也许我可以引入一种新的抽象,将依赖项与我的其余代码隔离开来(这将意味着更少的测试需要它)。 可能被测试的代码做的太多了 也许我需要注入更智能的对象,而不是一长串原语 也许我违反了“告诉-不要问”的规定 也许昂贵的代码可以做得更快(很少-注意这里的过早优化)

在所有这些情况下,我可以使用rspec魔法来缓解困难测试的症状,也可以尝试解决原因。我觉得过去几年我在前者上花费了太多时间,现在我想要一些更好的代码。

回答最初的问题:我宁愿不使用,但我仍然使用let。我主要使用它来适应团队其他成员的风格(似乎世界上大多数Rails程序员现在都深入到他们的rspec魔法中,所以这是经常发生的)。有时,当我在一些我无法控制的代码中添加测试,或者没有时间重构到更好的抽象时,我就会使用它:即当唯一的选择是止痛药时。