RSpec 3 计划
Myron Marston
2013年7月15日更新:现在已经有了日语翻译。
RSpec 2.0 于 2010 年 10 月发布。自那以后的近三年时间里,我们能够不断改进 RSpec,而无需进行重大更改,但我们已经达到一个阶段,RSpec 积累了不少代码冗余,这是为了保持与早期 2.x 版本向后兼容性而导致的。
RSpec 2.14 将是最后一个 RSpec 2 功能版本。(我们可能会发布一些错误修复补丁版本)。我们正在开始 RSpec 3 的开发工作,并且我想分享我们对 RSpec 未来方向的思考。
当然,这一切都还没有确定,最终,RSpec 成为一个成功的项目,是因为所有使用它的人。因此,如果您对我们应该采取的 RSpec 3 方向有任何想法,请说出来!
将被移除的内容
不再支持 1.8.6 和 1.9.1
RSpec 2.x 继续支持 Ruby 1.8.6,即使 MRI 团队已经不再支持该版本。作为 Ruby 生态系统中重要的测试基础设施的一部分,我们认为让 gem 作者决定何时放弃对旧版本 Ruby 的支持很重要,而不是因为他们选择的测试框架不再支持其中一个版本而被迫过早地放弃支持。
Ruby 1.8.6 和 1.9.1 已经 近两年 没有在 Travis 上可用,而且没有 CI 服务器在这些版本上运行我们的构建,继续支持它们变得非常困难。实际上,在过去几年中,我们实际上只是“半支持”了这些 Ruby 版本:当用户报告这些 Ruby 版本上的问题时,我们会修复它们,但我们没有在支持方面投入更多精力。
因此,现在是时候放弃对这些版本的支持了。我们计划在 RSpec 3 中继续支持 1.8.7、1.9.2 和所有更新的 Ruby 版本。鉴于 1.8.7 现在已经是遗留版本,我们可能会在 RSpec 4 中放弃对 1.8.7 的支持,尽管如果/当 Travis 在此之前停止支持它时,我们只能以与我们“半支持”1.8.6 相同的方式“半支持”它。
核心:its
将被移到一个外部 gem 中
我之前写过这方面的内容,所以这里就不赘述了。我们计划将 its
从 rspec-core 移到一个外部 gem 中。
期望:have(x).items
匹配器将被移到一个外部 gem 中
RSpec 的起源是在 Cucumber/Gherkin 出现之前,它的早期目标之一是使用项目利益相关者能够理解的自然语言来表达事物。在早期,像 team.should have(9).players
这样的表达式对于项目的目标来说是有意义的。从那时起,Cucumber/Gherkin 已经成为针对利益相关者测试的更好替代方案,而 RSpec 现在很少用于该目的。have(x).items
匹配器系列(包括 have_at_least(x).items
和 have_at_most(x).items
子级)过于复杂,而像 expect(team.players.size).to eq(9)
这样的简单表达式就足够了。
我们计划将这些匹配器从 rspec-expectations
移到一个外部 gem 中。
核心:不再有显式调试器支持
RSpec 长期以来一直支持 -d
/ --debug
命令行选项,用于通过 ruby-debug
gem 启用调试器。但是,如今 ruby-debug
并非唯一(甚至不是主要的)正在使用的调试 gem。debugger 已经成为 MRI 1.9.2+ 事实上的标准调试 gem,许多开发人员更喜欢使用 pry 进行调试。其他 Ruby 解释器(如 Rubinius)具有 自己的调试器。
我们计划在 RSpec 3 中移除显式调试器支持。除了移除命令行选项之外,我们还将移除 Kernel 中调试器的猴子补丁(当 ruby-debug 未加载时),因此当调试器未加载时,您将从 debugger
获取 NoMethodError
。
如果您想继续使用命令行选项加载调试器,您可以使用 require 标志(-r
),使用类似 -rdebugger
的选项。
核心:不再集成 RCov
RSpec::Core::RakeTask
长期以来一直具有一些 RCov 选项。RCov 仅适用于 MRI 1.8,如今大多数 Ruby 开发人员使用 SimpleCov 来满足其代码覆盖率需求。SimpleCov 与 RSpec(或任何测试框架)的集成非常简单,无需 RSpec 本身提供显式支持。
核心:Autotest 集成将被移到一个外部 gem 中
Autotest 曾经是主要的 Ruby 持续测试运行器。如今,guard 似乎是更受欢迎的选择,RSpec 的 Autotest 集成没有理由保留在 rspec-core 中。
核心:TextMate 格式化程序将被移到 TextMate 捆绑包中
多年来,TextMate 是 Ruby 开发人员使用的最受欢迎的文本编辑器。RSpec 长期以来一直有一个 特定于 TextMate 的格式化程序。如今,TextMate 在 Ruby 开发人员中的受欢迎程度远不如以前,TextMate 格式化程序没有充分的理由保留在 rspec-core 中。
大量弃用
RSpec 2.14 包含了许多在过去几年中已被弃用的内容。我们计划移除几乎所有已弃用的 API 和功能。
旧的期望/模拟语法怎么样?
RSpec 2.11 引入了 一种新的基于 expect
的 rspec-expectations 语法。 在 RSpec 2.14 中,我们更新了 rspec-mocks 以使用类似的语法。自从引入新的语法以来,我收到了许多关于我们何时会弃用或移除旧的基于 should
的语法的问题。
虽然我不会说“永远不会”(谁知道未来会发生什么?),但我们目前没有任何计划完全移除旧语法。用户在使用旧语法的代码上投入了多年的时间,虽然我们建议使用新的语法(特别是对于新项目),但如果我们很快移除旧语法,将会对用户不利。这也并非显著的维护负担。
对于 RSpec 3,我们考虑过默认禁用旧语法,迫使用户选择使用它。但是,我认为这样做对通过非最新教程接触 RSpec 的新用户不利。对于第一次尝试 RSpec 的人来说,从教程中复制的示例得到 NoMethodError
非常令人沮丧。有经验的用户可以轻松地禁用旧语法,而新用户可能没有足够的 RSpec 知识来知道如何启用他们的教程使用的旧语法。
话虽如此,我们确实希望鼓励人们切换到新的语法,因此我们计划让 RSpec 3 在第一次使用任何旧语法方法(should
、should_not
、should_receive
等)时打印警告,除非已显式启用 should
语法。这应该会促使人们转向新的语法,同时保持 RSpec 对新用户友好,并将为在 RSpec 4 中默认禁用旧语法铺平道路。
新增内容
零猴子补丁模式!
从历史上看,RSpec 广泛使用猴子补丁来创建其可读语法,为每个对象添加了 describe
、shared_examples_for
、shared_context
、should
、should_not
、should_receive
、should_not_receive
和 stub
等方法。在最近的几个 2.x 版本中,我们一直在努力减少 RSpec 执行的猴子补丁数量。
- 从 rspec-core 2.11 开始,
describe
不再添加到每个对象。相反,它只被添加到顶层main
对象和Module
(以便它可以在类和模块中使用)。 - 在 rspec-expectations 2.11 中,我们添加了
expect
语法,并提供了禁用should
语法的配置选项——这将从每个对象中移除should
和should_not
。 - 从 rspec-core 2.12 开始,
shared_examples_for
和shared_context
不再被添加到每个对象。与describe
一样,它们只被添加到顶层main
对象和Module
。 - 在 rspec-mocks 2.14 中,我们更新了
rspec-mocks
以支持基于expect
的语法,并提供了禁用旧模拟语法的配置选项——这将从每个对象中移除should_receive
、should_not_receive
和stub
。
如上所述,我们将在 3.0 中移除 RSpec 的猴子补丁 Kernel#debugger
。我们还计划提供一个配置选项来移除顶层 DSL 方法(describe
、shared_examples_for
等)到 main
和 Module
的猴子补丁,而是要求您在这些方法前加上 RSpec.
。
RSpec.describe MyClass do
# Within an example group you'll still be able to use
# a bare `describe`:
describe "#some_method" do
end
# And you'll be able to use a bare `shared_examples_for`:
shared_examples_for "something" do
end
end
RSpec.shared_examples_for "some behavior" do
end
最终结果将是一组配置选项(rspec-expectations
一个,rspec-mocks
一个,rspec-core
一个),这将为 RSpec 提供零猴子补丁模式。(我们也可能会提供一个统一的配置选项,它将设置所有三个选项)。
我们计划让这些配置选项在 RSpec 4.0 中成为默认值,这样 RSpec 4.0 将默认拥有零猴子补丁。
模拟:测试双重接口验证
不幸的是,让您的测试双重与它们模拟的实际接口不同步非常容易。当您重命名方法或更改方法期望的参数数量时,您很容易忘记更新作为已更改类的替身使用的测试双重。
我一直是 rspec-fire 解决这个问题方法的粉丝。我计划将它的一个版本移植到 rspec-mocks 中。
请查看 github 问题,在那里我们正在讨论这个问题的全部细节(此功能的 API 和语义当然还没有确定,所以请在该问题上表达您的想法!)。
期望:完全可组合的匹配器
在 RSpec 2.13 中,我们 添加了支持 让 include
匹配器接受一个匹配器列表以进行匹配。这种可组合性非常有用,我们计划在 RSpec 3 中将其扩展到所有匹配器。例如,您可以使用类似的表达式:
expect { |b|
some_object.do_something(&b)
}.to yield_with_args(include(match(/foo/), match(/bar/)))
这表达了一个详细的期望:“我希望 some_object.do_something
以一个包含匹配 /foo/
的字符串和匹配 /bar/
的字符串的集合来产生。”
我们还在考虑添加匹配器别名,这些别名在以这种方式组合时更易读,这样您就可以这样写:
expect { |b|
some_object.do_something(&b)
}.to yield_with_args(a_collection_including(a_string_matching(/foo/),
a_string_matching(/bar/)))
有关更多详细信息或对这个问题发表意见,请查看 github 问题。
核心:格式化程序 API 改进
当前用于向格式化程序通知测试套件进度的 API 在添加新的通知和更改现有通知方面被证明有些僵化。我们计划在几个方面进行更改。
- 与其要求所有格式化程序都实现所有通知方法,不如让格式化程序可以订阅特定通知。这将允许您实现格式化程序所需的最小集,并将允许我们在不担心破坏现有格式化程序的情况下添加新的通知。
- 通知参数将从有序参数列表更改为单个值对象,这将使我们能够轻松地向特定通知添加额外数据,而无需更改方法签名。
这些更改将使我们能够进行进一步的改进,这些改进在 2.x 版本中无法实现。我们还计划在 RSpec 3 中提供一个兼容性层,该层将包装针对旧 API 编写的格式化程序并将其适配到新 API,以便用户在依赖旧格式化程序时能够更轻松地升级。
有关更多详细信息,请查看 github 问题。
核心:DSL 方法将产生示例
在 RSpec 2 中,当前运行的示例被公开为 example
。它可以用来 访问示例的元数据。这偶尔会 造成问题,因为用户无意中定义了自己的 example
方法。在 RSpec 3 中,我们将删除 example
方法,选择从在示例上下文中运行的每个 DSL 方法中产生示例
describe MyClass do
before(:each) { |example| }
subject { |ex| }
let(:user) { |ex| User.find(ex.metadata[:user_id]) }
# before(:all) will NOT yield an example
it "can access the current example using a block local" do |example|
# do something with `example`
end
end
我们知道,这可能会给依赖于使用 example
API 的 gem(例如 Capybara)的用户带来升级方面的麻烦。我们正在讨论如何使升级过程更加顺利,无论是对于用户还是 gem 作者。有关更多信息,请参阅 github 问题。
期望:Matcher 协议和自定义 matcher API 更改
虽然 RSpec 一直在远离其基于 should
的语法,但 matcher 协议和自定义 matcher API 并没有相应地改变。matcher 协议仍然依赖于诸如 failure_message_for_should
和 failure_message_for_should_not
之类的方法,而自定义 matcher API 则具有诸如 match_for_should
和 match_for_should_not
之类的方法。
在 RSpec 3 中,我们希望更改 matcher 协议和自定义 matcher API,使其不再以 should
术语进行表述,同时仍然保留向后兼容层,以便现有 matcher 能够继续工作,并计划在 RSpec 4 中删除该兼容层。我们还不确定新的 API 会是什么样子;如果您有想法,请 在 github 问题中发表您的意见。
模拟:any_instance
块实现将产生接收者
当使用 any_instance
伪造方法时,您可以像普通伪造一样传递一个块实现。但是,如果您想在块中访问接收者(即接收消息的实例),则没有办法实现这一点。在 RSpec 3 中,我们将纠正这个疏忽,接收者将作为第一个块参数产生。
allow_any_instance_of(User).to receive(:age) do |user|
((Date.today - user.birthdate) / 365).floor
end
为了向后兼容,我们将添加一个配置选项来禁用此行为。
升级路径
尽管 RSpec 3.0 将是一个主要版本,它将允许我们自 2010 年以来首次进行有意性的重大更改,但对我们来说,现有测试套件的升级路径尽可能简单至关重要。为此,我们计划发布一个 2.99 版本,它将纯粹是为了帮助用户升级而存在。以下是我们考虑的内容。
- RSpec 2.99 将是 RSpec 2.14 加上一些针对将在 3.0 中删除的内容的额外弃用警告。
- 您应该能够将现有的 RSpec 2.x 测试套件升级到 2.99 而无需进行任何更改。
- RSpec 2.99 将针对我们在 3.0 中删除或以某种方式破坏的任何内容向您发出弃用通知。您应该解决这些警告。一旦您在 2.99 上没有警告,您应该能够升级到 3.0,而无需进行任何进一步的更改。
2.99 版本将是一个重要的步骤,在升级过程中不应跳过它。它将为您提供一个专门针对您的测试套件对 RSpec 的使用的升级清单,为您提供比梳理变更日志试图找出 RSpec 3 中的所有更改更简单、更有效的方式来进行升级。
开发和发布计划
我们已经开始在每个 RSpec 存储库的主分支上开发 RSpec 3。我们还拥有一个 2-14-maintenance 分支用于 2.14 更改(即用于可能的补丁版本)和一个 2-99-maintenance 分支用于将要进入 2.99 的更改。我们计划在向最终 3.0 版本发布的过程中进行多次发布候选版(以及可能的一些测试版)。
我不会猜测我们何时发布 RSpec 3。经验告诉我,软件发布日期估计总是错误的 :(。
“我该如何帮助?”
当前的 RSpec 核心团队(David、Andy、Jon、Sam、Bradley 和 我自己)将推动 3.0 版本的工作……但一如既往,我们很乐意得到社区的帮助。以下是一些您可以提供帮助的具体方法。
- 如上所述,我们计划将 RSpec 的一些功能提取到一个外部 gem(
its
、have
matcher 和 autotest 集成)。如果能找到社区中的成员来接手这些 gem 的维护(也许还可以进行初始提取,如果您有兴趣的话),那就太好了。 - 我们重视社区对 RSpec 发展方向的反馈,因此,如果您有想法或建议,请在上面链接的 issues(以及其他任何 issues,实际上,RSpec 3 中的 issues 比我在这里介绍的要多!)中发表评论。
- 如果您想帮助为 RSpec 贡献代码,那太棒了!但是,在此期间,各种 RSpec gem 的代码库将出现比平时更多的变化,从而造成更多合并冲突与拉取请求,并使我们更难整合来自用户的贡献。因此,如果您想贡献,请在投入大量精力进行 PR 之前与我们联系(通过在 github issues 上发表评论和/或在 irc.freenode.net 上的 #rspec 频道与我们聊天)。事先进行少量沟通将有助于我们轻松地整合您的贡献。
- 您可以提供帮助的最重要的方式之一是尝试 2.99 和 3.0 的预发布版本并给我们反馈。请继续关注发布公告。
- rspec.info 急需更新,与 RSpec 3 的发布同步发布该网站的新版本将非常棒。但是,RSpec 核心团队很难抽出时间来处理网站,我们中的大多数人更专注于后端而不是前端开发。如果一些社区成员能够站出来并在这一领域提供帮助,那就太好了!