RSpec 3 中的显著变化

Myron Marston

2014 年 5 月 21 日

更新:现在有 日语翻译

RSpec 3.0.0 RC1 已经发布几天了,3.0.0 正式版也即将发布。我们过去 6 个月一直在使用测试版,很高兴能与大家分享。以下是新功能

所有 gem

删除对 Ruby 1.8.6 和 1.9.1 的支持

这些版本的 Ruby 已经很久以前就停止维护了,RSpec 3 不支持它们。

改进 Ruby 2.x 支持

最近发布的 RSpec 2.x 版本(即在 Ruby 2.0 发布后发布的版本)已正式支持 Ruby 2,但 RSpec 3 的支持得到了极大改善。我们现在提供对使用 Ruby 2 新功能的支持,例如关键字参数和预置模块。

新的 rspec-support gem

rspec-support 是一个新的 gem,我们正在使用它来处理多个 rspec-(core|expectations|mocks|rails) 共享的通用代码。它目前不包含任何供最终用户或扩展库作者使用的公共 API,但我们可能会在将来公开部分 API。

如果您从 github 中获取最新版本的 RSpec,您还需要从 github 中获取 rspec-support。

强大、经过良好测试的升级过程

RSpec 3 中的每一个重大更改在 2.99 中都有相应的弃用警告。在测试版期间,我们进行了多次升级,以确保此过程尽可能顺利。我们已经整理了逐步升级说明.

升级过程还突出显示了 RSpec 的新弃用系统,该系统高度可配置(允许您将弃用输出到文件或将所有弃用转换为错误),并旨在最大程度地减少重复的弃用输出。

改进的文档

我们投入了大量精力更新所有 gem 的 API 文档。它们目前托管在 rubydoc.info

…但我们目前正在更新 rspec.info 以自行托管它们。

虽然文档仍在开发中(坦率地说,永远不会停止开发),但我们已经确保明确声明所有公共 API 作为 SemVer 兼容性的一部分。我们绝对致力于在所有 3.x 版本中维护所有公共 API。另一方面,私有 API 被标记为私有 API,因为我们希望在任何 3.x 版本中保留随意更改它们的灵活性。

请勿使用我们声明为私有的 API。如果您发现自己需要现有公共 API 未满足的 API,请询问。我们很乐意为您的需求公开私有 API 或添加新的 API 来满足您的用例。

gem 现在已签名

我们已经开始对我们的 gem 版本进行签名。虽然当前的 gem 签名系统远非理想,并且 更好的解决方案 正在开发中,但它总比没有好。我们已经将我们的公共证书放在 GitHub 上。

有关当前 gem 签名系统的更多详细信息,请参阅 使用签名 Ruby gem 的实用指南.

零猴子补丁模式

RSpec 现在可以在没有任何猴子补丁的情况下使用。这方面的大部分基础工作都是在最近的 2.x 版本中完成的,这些版本在 rspec-expectations 和 rspec-mocks 中添加了新的 expect 基于语法。我们在 RSpec 3 中完成了剩下的工作,并为剩下的猴子补丁提供了替代方案。

为了方便起见,您可以使用一个选项禁用所有猴子补丁

# spec/spec_helper.rb
RSpec.configure do |c|
  c.disable_monkey_patching!
end

感谢 Alexey Fedorov 实现 此配置选项。

更多信息

rspec-core

钩子范围的新名称::example:context

RSpec 2.x 有三个不同的钩子范围

describe MyClass do
  before(:each) { } # runs before each example in this group
  before(:all)  { } # runs once before the first example in this group
end
# spec/spec_helper.rb
RSpec.configure do |c|
  c.before(:each)  { } # runs before each example in the entire test suite
  c.before(:all)   { } # runs before the first example of each top-level group
  c.before(:suite) { } # runs once after all spec files have been loaded, before the first spec runs
end

有时,用户对 :each:all 的含义感到困惑,尤其是 :all 在配置块中使用时会令人困惑

# spec/spec_helper.rb
RSpec.configure do |c|
  c.before(:all) { }
end

在这种情况下,术语 :all 暗示此钩子将在套件中所有示例之前运行一次——但这正是 :suite 的作用。

在 RSpec 3 中,:each:all 有别名,使它们的范围更加明确::example:each 的别名,:context:all 的别名。请注意,:each:all 被弃用,我们也没有计划这样做。

感谢 John Feminella 实现 这项功能。

更多信息

DSL 方法将示例作为参数传递

RSpec::Core::Example 提供对示例的所有详细信息的访问:其描述、位置、元数据、执行结果等。在 RSpec 2.x 中,示例通过 example 方法公开,该方法可以从任何钩子或单个示例中访问

describe MyClass do
  before(:each) { puts example.metadata }
end

在 RSpec 3 中,我们已删除 example 方法。相反,示例实例被传递给所有示例范围的 DSL 方法作为显式参数

describe MyClass do
  before(:example) { |ex| puts ex.metadata }
  let(:example_description) { |ex| ex.description }

  it 'accesses the example' do |ex|
    # use ex
  end
end

感谢 David Chelimsky 想出了这个主意并 实现 它!

更多信息

新的 expose_dsl_globally 配置选项以禁用 rspec-core 猴子补丁

RSpec 2.x 对 mainModule 进行猴子补丁,以提供顶级方法,例如 describeshared_examples_forshared_context

shared_examples_for "something" do
end

module MyGem
  describe SomeClass do
    it_behaves_like "something"
  end
end

在 RSpec 3 中,这些方法现在也适用于 RSpec 模块(除了仍然可以作为猴子补丁使用之外)

RSpec.shared_examples_for "something" do
end

module MyGem
  RSpec.describe SomeClass do
    it_behaves_like "something"
  end
end

您可以通过将新的 expose_dsl_globally 配置选项设置为 false 来完全删除 rspec-core 的猴子补丁(这将导致上面的第一个示例引发 NoMethodError

# spec/spec_helper.rb
RSpec.configure do |config|
  config.expose_dsl_globally = false
end

感谢 Jon Rowe 实现 这项功能。

更多信息

使用 alias_example_group_to 定义示例组别名

在 RSpec 2.x 中,我们提供了一个 API,允许您定义具有附加元数据的 example 别名。例如,这在内部用于定义 fit 作为 it 的别名,并带有 :focus => true 元数据

# spec/spec_helper.rb
RSpec.configure do |config|
  config.alias_example_to :fit, :focus => true
end

在 RSpec 3 中,我们已将此功能扩展到示例组

# spec/spec_helper.rb
RSpec.configure do |config|
  config.alias_example_group_to :describe_model, :type => :model
end

您可以在使用 rspec-rails 的项目中使用此示例,并使用 describe_model User 而不是 describe User, :type => :model

感谢 Michi Huber 实现 这项功能。

更多信息

新的示例组别名:xdescribexcontextfdescribefcontext

除了包含定义示例组别名的 API 之外,我们还包含了几个额外的内置别名(除了 describecontext 之外)

更多信息

pending 语义的更改(以及 skip 的引入)

现在运行挂起的示例以检查它们是否确实通过。如果挂起块失败,则它将像以前一样标记为挂起。但是,如果它成功,则会导致失败。这有助于确保挂起的示例有效,并且在实现其描述的行为时会及时处理它们。

为了支持旧的“从不运行”行为,添加了 skip 方法和元数据。以下所有示例都不会运行

describe Post do
  skip 'not implemented yet' do
  end

  it 'does something', :skip => true do
  end

  it 'does something', :skip => 'reason explanation' do
  end

  it 'does something else' do
    skip
  end

  it 'does something else' do
    skip 'reason explanation'
  end
end

通过此更改,在示例中向 pending 传递块不再有意义,因此已删除该行为。

感谢 Xavier Shay 实现 这项功能。

更多信息

用于单行语句的新 API:is_expected

RSpec 多年来一直具有单行语句语法

describe Post do
  it { should allow_mass_assignment_of(:title) }
end

在这种情况下,should 不是可以被移除的猴子补丁 should,可以通过将 rspec-expectations 配置为仅支持 :expect 语法来移除它。它没有猴子补丁 Objectshould 相伴而来的包袱,并且始终可用,无论您的语法配置如何。

一些用户对这种 should 如何与 expect 语法相关联以及您是否可以继续使用它表示困惑。它将在 RSpec 3 中继续可用(再次,无论您的语法配置如何),但我们还添加了一个更符合 expect 语法的备用 API

describe Post do
  it { is_expected.to allow_mass_assignment_of(:title) }
end

is_expected 定义非常简单,定义为 expect(subject),并且还通过 is_expected.not_to matcher 支持否定期望。

更多信息

示例组可以单独排序

RSpec 2.8 将随机排序引入 RSpec,这对发现规范套件中无意中的排序依赖关系非常有用。在 RSpec 3 中,它不再是一个全有或全无的功能。您可以通过使用适当的元数据标记它们来控制单个示例组的排序方式

describe MyClass, :order => :defined do
  # examples in this group will always run in defined order,
  # regardless of any other ordering configuration.
end

describe MyClass, :order => :random do
  # examples in this group will always run in random order,
  # regardless of any other ordering configuration.
end

这对于从定义的排序迁移到随机排序特别有用,因为它允许您在针对特定组选择加入此功能时,逐一处理排序依赖关系,而不是必须一次性解决所有问题。

作为此的一部分,我们还将 --order default 重命名为 --order defined,因为我们意识到“默认”是一个高度重叠的术语。

感谢 Andy LindemanSam Phippen 帮助 实现 此功能。

更多信息

新的排序策略 API

在 RSpec 3 中,我们对排序策略 API 进行了彻底的修改。过去曾经是 三个 不同 方法 现在成为了一种方法:register_ordering。使用它来定义一个名为排序策略

# spec/spec_helper.rb
RSpec.configure do |config|
  config.register_ordering(:description_length) do |list|
    list.sort_by { |item| item.description.length }
  end
end
describe MyClass, :order => :description_length do
  # ...
end

或者,您可以使用它来定义全局排序

# spec/spec_helper.rb
RSpec.configure do |config|
  config.register_ordering(:global) do |list|
    # sort them alphabetically
    list.sort_by { |item| item.description }
  end
end

:global 排序用于对顶层示例组进行排序,以及对没有 :order 元数据的示例组进行排序。

更多信息

rspec --init 改进

rspec 命令长期以来一直提供 --init 选项来设置项目的骨架。在 RSpec 3 中,它生成的 文件已得到很大改进,以提供更好的开箱即用体验,并提供一个具有更多推荐设置的 spec/spec_helper.rb 文件。

请注意,生成的 文件中已注释掉不打算成为未来默认值的推荐设置,因此打开此文件并接受您想要的推荐设置是个好主意。

更多信息

新的 --dry-run CLI 选项

此选项将打印规范套件的格式化输出,而不会运行任何示例或钩子。它作为一种查看规范套件的文档输出而无需等待规范运行或担心其通过/失败状态的方法特别有用。

感谢 Thomas Stratmann 贡献 此功能!

更多信息

格式化程序 API 更改

添加了一个全新的格式化程序 API,它更加灵活。

新的格式化程序看起来像这样

class CustomFormatter
  RSpec::Core::Formatters.register self, :example_started

  def initialize(output)
    @output = output
  end

  def example_started(notification)
    @output << "example: " << notification.example.description
  end
end

为了继续支持旧的 2.x 格式化程序 API,提供了 rspec-legacy_formatters gem

感谢 Jon Rowe 负责此项工作。

更多信息

断言配置更改

虽然大多数用户使用 rspec-expectations,但使用其他东西也很容易,并且 RSpec 2.x 通过配置选项使最常见的备选方案轻松可用

# spec/spec_helper.rb
RSpec.configure do |config|
  config.expect_with :stdlib
  # or, to use both:
  config.expect_with :stdlib, :rspec
end

但是,围绕 :stdlib 存在一些混淆。在 Ruby 1.8 上,标准库断言模块是 Test::Unit::Assertions。在 1.9+ 上,它是 Minitest::Assertions 的一个薄包装器(通常您最好只使用它)。同时,还有一个 test-unit gem 定义了 Test::Unit::Assertions(它不是 minitest 的包装器)和一个 minitest gem。

对于 RSpec 3,我们已删除 expect_with :stdlib,而是选择了明确的 :test_unit:minitest 选项

# spec/spec_helper.rb
RSpec.configure do |config|
  # for test-unit:
  config.expect_with :test_unit

  # for minitest:
  config.expect_with :minitest
end

感谢 Aaron Kromer实现此功能

更多信息

定义派生元数据

RSpec 的元数据系统非常灵活,允许您以多种方式对测试套件进行切片和切块。有一个新的配置 API 允许您定义派生元数据。例如,要使用 :js => true 自动标记 spec/acceptance/js 中的所有示例组

# spec/spec_helper.rb
RSpec.configure do |config|
  config.define_derived_metadata(:file_path => %r{/spec/acceptance/js/}) do |metadata|
    metadata[:js] = true
  end
end

更多信息

移除

一些不再是 RSpec 核心的东西要么被完全删除,要么被提取到外部 gem 中

rspec-expectations

在没有显式启用 should 语法的情况下使用它已被弃用

在 RSpec 2.11 中,我们开始通过 引入新的基于 expect 的语法 来消除 RSpec 中的猴子修补。在 RSpec 3 中,我们保留了 should 语法,它默认可用,但如果您在没有显式启用它的情况下使用它,您将收到弃用警告。这将为在 RSpec 4 中将其默认禁用(或可能提取到单独的 gem 中)铺平道路,同时最大限度地减少通过旧教程来到 RSpec 的新手的混淆。

我们认为 expect 语法现在是 RSpec 的“主要”语法,但如果您更喜欢旧的基于 should 的语法,请随意继续使用它:我们没有计划永远杀死它。

感谢 Sam Phippen实现 此功能。

更多信息

复合匹配器表达式

在 RSpec 3 中,您可以使用 andor 将多个匹配器链接在一起

# these two expectations...
expect(alphabet).to start_with("a")
expect(alphabet).to end_with("z")

# ...can be combined into one expression:
expect(alphabet).to start_with("a").and end_with("z")

# You can also use `or`:
expect(stoplight.color).to eq("red").or eq("green").or eq("yellow")

它们是 &| 运算符的别名

expect(alphabet).to start_with("a") & end_with("z")
expect(stoplight.color).to eq("red") | eq("green") | eq("yellow")

感谢 Eloy Espinaco建议并实现 此功能,以及 Adam Farhi使用 &| 运算符扩展它

更多信息

可组合匹配器

RSpec 3 允许您通过将匹配器作为参数传递给其他匹配器来表达详细的意图

s = "food"
expect { s = "barn" }.to change { s }.
  from( a_string_matching(/foo/) ).
  to( a_string_matching(/bar/) )

expect { |probe|
  "food".tap(&probe)
}.to yield_with_args( a_string_starting_with("f") )

为了提高代码表达式和失败消息的可读性,大多数匹配器都有别名,这些别名在这些表达式中作为参数传递时可以正确读取。

更多信息

match 匹配器可用于数据结构

在 RSpec 3 之前,match 匹配器存在于使用 #match 方法执行字符串/正则表达式匹配。

expect("food").to match("foo")
expect("food").to match(/foo/)

在 RSpec 3 中,它还支持匹配任意嵌套的数组/哈希数据结构。预期值可以使用任何级别的嵌套匹配器来表达

hash = {
  :a => {
    :b => ["foo", 5],
    :c => { :d => 2.05 }
  }
}

expect(hash).to match(
  :a => {
    :b => a_collection_containing_exactly(
      an_instance_of(Fixnum),
      a_string_starting_with("f")
    ),
    :c => { :d => (a_value < 3) }
  }
)

更多信息

新的 all 匹配器

此匹配器允许您指定集合中的所有项目都为真。传递一个匹配器作为参数

expect([1, 3, 5]).to all( be_odd )

感谢 Adam Farhi贡献 此功能!

更多信息

新的 output 匹配器

此匹配器可用于指定块写入 stdout 或 stderr

expect { print "foo" }.to output("foo").to_stdout
expect { print "foo" }.to output(/fo/).to_stdout
expect { warn  "bar" }.to output(/bar/).to_stderr

感谢 Matthias Günther建议 此功能(以及推动它)以及 Luca Pette完成此功能

更多信息

新的 be_between 匹配器

RSpec 2 为实现 between? 的对象提供了 be_between 匹配器,使用动态谓词支持。在 RSpec 3 中,我们正在获得一个更好的 be_between 匹配器,它在几个方面更好

# like `Comparable#between?`, it is inclusive by default
expect(10).to be_between(5, 10)

# ...but you can make it exclusive:
expect(10).not_to be_between(5, 10).exclusive

# ...or explicitly label it inclusive:
expect(10).to be_between(5, 10).inclusive

感谢 Erik Michaels-Ober贡献 此功能,以及 Pedro Gimenez改进 它!

更多信息

布尔匹配器已重命名

RSpec 2 有一对匹配器(be_truebe_false),它们反映了 Ruby 的条件语义:be_true 将对除 nilfalse 之外的任何值都通过,而 be_false 将对 nilfalse 通过。

在 RSpec 3 中,我们已将它们重命名为 be_truthybe_falsey(或者 be_falsy,如果你更喜欢这个拼写),以使它们的语义更明确,并减少与 be true/be false(它们与 be_true/be_false 读起来一样,但只有在给出确切的 true/false 值时才会通过)的混淆。

感谢 Sam Phippen实现 此功能。

更多信息

match_array 匹配器现在可作为 contain_exactly 使用

RSpec 长期以来都有一个匹配器,它允许您匹配两个数组的内容,同时忽略任何排序差异。最初,这是使用 =~ 运算符与旧的 should 语法一起使用的

[2, 1, 3].should =~ [1, 2, 3]

后来,当我们 添加 expect 语法 时,我们决定不将运算符匹配器转发到新语法,而是将匹配器称为 match_array

expect([2, 1, 3]).to match_array([1, 2, 3])

match_array 是我们当时能想到的最佳名称,但我们对它并不太满意:“match”是一个不精确的术语,而匹配器旨在用于除数组之外的其他类型的集合。我们在 RSpec 3 中为它想出了一个更好的名称

expect([2, 1, 3]).to contain_exactly(1, 2, 3)

请注意,match_array 没有被弃用。这两种方法的行为完全相同,只是 contain_exactly 接受单独展开的项目,而 match_array 接受单个数组参数。

更多信息

集合基数匹配器已提取到 rspec-collection_matchers gem 中

集合基数匹配器——have(x).itemshave_at_least(y).itemshave_at_most(z).items——是 RSpec 中最“神奇”和令人困惑的部分之一。它们已被 提取rspec-collection-matchers gem 中,Hugo Baraúna 慷慨地自愿维护它。

一般替代方法是对集合的大小设置期望

expect(list).to have(3).items
# ...can be written as:
expect(list.size).to eq(3)

expect(list).to have_at_least(3).items
# ...can be written as:
expect(list.size).to be >= 3

expect(list).to have_at_most(3).items
# ...can be written as:
expect(list.size).to be <= 3

改进的 Minitest 集成

在 RSpec 2.x 中,rspec-expectations 会 自动包含自身MiniTest::Unit::TestCaseTest::Unit::TestCase 中,以便您只需加载它就可以从 Minitest 或 Test::Unit 使用 rspec-expectations。

在 RSpec 3 中,我们以几种方式更新了此集成

更多信息

匹配器协议的更改

如上所述,在 RSpec 3 中,我们不再认为 should 是 rspec-expectations 的主要语法。我们已更新匹配器协议以反映这一点

此外,我们添加了 supports_block_expectations? 作为匹配器协议的新可选部分。这用于在用户在块期望表达式中错误地使用值匹配器时为他们提供清晰的错误。例如,在更改之前,在使用 be_nil 之类的匹配器时将块传递给 expect 可能导致误报

expect { foo.bar }.not_to be_nil

# ...is equivalent to:
block = lambda { foo.bar }
expect(block).not_to be_nil

# ...but the block is not nil (even though `foo.bar` might return nil),
# so the expectation will pass even though the user probably meant:
expect(foo.bar).not_to be_nil

请注意,supports_block_expectations? 是协议的可选部分。对于不打算在块期望表达式中使用的匹配器,您不需要定义它。

更多信息

rspec-mocks

在没有显式启用它的情况下使用猴子修补语法已被弃用

与 rspec-expectations 一样,我们一直在将 rspec-mocks 朝着零猴子修补语法方向发展。这最初是在 2.14 中引入的。在 RSpec 3 中,如果您在没有显式启用它(就像 rspec-expectations 的新语法一样)的情况下使用原始语法(例如 obj.stubobj.should_receive 等),您将收到弃用警告。

感谢 Sam Phippen实现 此功能。

receive_messagesreceive_message_chain 用于新语法

原始的猴子补丁语法有一些新语法(在 2.14 中发布)缺乏的功能。我们在 RSpec 3 中通过两个新的 API 解决了这个问题:receive_messagesreceive_message_chain

# old syntax:
object.stub(:foo => 1, :bar => 2)
# new syntax:
allow(object).to receive_messages(:foo => 1, :bar => 2)

# old syntax:
object.stub_chain(:foo, :bar, :bazz).and_return(3)
# new syntax:
allow(object).to receive_message_chain(:foo, :bar, :bazz).and_return(3)

这些新 API 的一个好处是它们也与 expect 一起工作,而在旧语法中没有 stub(hash)stub_chain 的消息期望等效项。

感谢 Jon RoweSam Phippen 为此功能的实现。

更多信息

删除了 doublemockstub 别名

历史上,rspec-mocks 提供了三种创建测试双重的方法:mockstubdouble。在 RSpec 3 中,我们去掉了 mockstub,只保留了 double,并构建了更多使用 double 命名法的功能(例如验证双重 - 参见下文)。

当然,虽然 RSpec 3 不再提供 doublemockstub 别名,但如果你想继续使用它们,可以很容易地在你自己定义这些别名。

# spec/spec_helper.rb
module DoubleAliases
  def mock(*args, &block)
    double(*args, &block)
  end
  alias stub mock
end

RSpec.configure do |config|
  config.include DoubleAliases
end

感谢 Sam Phippen 为此功能的 实现

更多信息

验证双重

添加了一种新的双重类型,它确保你只对实际存在的 method 进行 stub 或 mock,并且传递的参数符合声明的 method 签名。instance_doubleclass_doubleobject_double 双重将在不满足这些条件时抛出异常。如果该类尚未加载(通常在孤立地运行单元测试时),则不会抛出任何异常。

这是一种细微的行为,但非常强大,因为它允许孤立单元测试的速度与集成测试(或类型系统)更接近的信心。很少有理由不使用这些新的更强大的双重类型。

感谢 Xavier Shay 为此功能的理念和实现。

更多信息

部分双重验证配置选项

可以 全局启用部分双重的验证双重行为。(部分双重是指当你模拟或 stub 一个现有对象时:expect(MyClass).to receive(:some_message)。)

# spec/spec_helper.rb
RSpec.configure do |config|
  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end
end

我们建议你为所有新代码启用此选项。

范围更改

rspec-mocks 的操作是针对每个测试的生命周期设计的。这在 RSpec 2 中有记录,但在运行时并不总是明确强制执行,有时我们会收到用户在尝试在每个测试的生命周期之外使用 rspec-mocks 的功能时遇到的错误报告。

在 RSpec 3 中,我们已经加强了这一点,并且此生命周期在运行时明确强制执行。

我们还提供了一个新的 API,它允许你在任意位置(例如 before(:context) 钩子)创建临时范围。

describe MyWebCrawler do
  before(:context) do
    RSpec::Mocks.with_temporary_scope do
      allow(MyWebCrawler).to receive(:crawl_depth_limit).and_return(5)
      @crawl_results = MyWebCrawler.perform_crawl_on("http://some-host.com/")
    end # verification and resets happen when the block completes
  end

  # ...
end

感谢 Sam Phippen 帮助 实现 这些更改,以及 Sebastian Skałacki 为新的 with_temporary_scope 功能提出建议。

更多信息

any_instance 实现块会 yield 接收方

当为 method stub 提供一个实现块时,根据对象的 state 进行一些计算可能很有用。不幸的是,在 RSpec 2 中使用 any_instance 时,没有简单的办法做到这一点。在 RSpec 3 中,接收方将作为 any_instance 实现块的第一个参数 yield,这使得这变得很容易。

allow_any_instance_of(Employee).to receive(:salary) do |employee, currency|
  usd_amount = 50_000 + (10_000 * employee.years_worked)
  currency.from_usd(usd_amount)
end

employee = Employee.find(23)
salary = employee.salary(Currency.find(:CAD))

感谢 Sam Phippen 为此功能的 实现

更多信息

rspec-rails

文件类型推断默认情况下已禁用

rspec-rails 会根据规范在文件系统中的位置自动向规范添加元数据。这对新用户来说令人困惑,并且对于一些资深用户来说并不理想。

在 RSpec 3 中,必须明确启用此行为。

# spec/spec_helper.rb
RSpec.configure do |config|
  config.infer_spec_type_from_file_location!
end

由于这种假设的行为在教程中非常普遍,因此默认生成的配置仍然启用此行为。

要明确地标记规范而不使用自动推断,请设置 type 元数据。

RSpec.describe ThingsController, type: :controller do
  # Equivalent to being in spec/controllers
end

不同的可用类型在每种不同类型的规范中都有记录,例如 控制器规范的文档

更多信息

提取了 activemodel 模拟支持

mock_modelstub_model 已提取到 rspec-activemodel-mocks gem 中。

感谢 Thomas Holmes 完成提取并提供维护新 gem 的帮助。

删除了 webrat 支持

Webrat 支持已被移除。使用 capybara 代替。

匿名控制器改进

rspec-rails 长期以来允许你创建用于测试的匿名控制器。在 RSpec 3 中,它们已经得到了一些改进。

更多信息

最后的话

与往常一样,每个子项目的完整变更日志都可用。

RSpec 3 是 RSpec 近 4 年来的第一个主要版本。它代表了来自众多贡献者的巨大工作量。

无论你如何使用 RSpec,我们都希望你喜欢这些新变化,就像我们一样。

感谢 Xavier Shay 帮助撰写这篇博客文章,感谢 Jon Rowe、Sam Phippen 和 Aaron Kromer 校对。