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

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

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


当前回答

我使用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是惰性求值的,不要在其中放置副作用方法,否则您将无法轻松地从let更改到(:each)之前。 你可以用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()之间的区别在于let()是延迟求值的。这意味着let()直到它定义的方法第一次运行时才会被求值。

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

在我的rspec测试中,我已经完全用let()代替了所有实例变量的使用。我已经为一个朋友写了一个简单的例子,他用它来教一个小的Rspec课程:http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html

正如这里的一些其他答案所说,let()是延迟计算的,因此它只加载需要加载的对象。它dry了规范,使其更具可读性。事实上,我已经将Rspec let()代码移植到我的控制器中,以inherited_resource gem的风格使用。http://ruby-lambda.blogspot.com/2010/06/stealing-let-from-rspec.html

除了惰性求值外,另一个优点是,结合ActiveSupport::Concern和load-everything-in规范/support/行为,您可以创建自己的特定于应用程序的规范mini-DSL。我已经编写了针对Rack和RESTful资源的测试。

我使用的策略是工厂-一切(通过机械师+伪造/Faker)。但是,可以将它与before(:each)块结合使用,为整个示例组预加载工厂,从而使规范运行得更快:http://makandra.com/notes/770-taking-advantage-of-rspec-s-let-in-before-blocks

我使用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

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