rspec-core

rspec-core 提供了编写代码行为的可执行示例的结构,以及一个 rspec
命令,其中包含用于限制运行哪些示例和定制输出的工具。
安装
gem install rspec # for rspec-core, rspec-expectations, rspec-mocks
gem install rspec-core # for rspec-core only
rspec --help
想要针对 main
分支运行?您还需要包含依赖的 RSpec 库。将以下内容添加到您的 Gemfile
中
%w[rspec rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main'
end
基本结构
RSpec 使用“describe”和“it”这两个词,以便我们可以表达类似对话的概念。
"Describe an order."
"It sums the prices of its line items."
RSpec.describe Order do
it "sums the prices of its line items" do
order = Order.new
order.add_entry(LineItem.new(:item => Item.new(
:price => Money.new(1.11, :USD)
)))
order.add_entry(LineItem.new(:item => Item.new(
:price => Money.new(2.22, :USD),
:quantity => 2
)))
expect(order.total).to eq(Money.new(5.55, :USD))
end
end
describe
方法创建一个 ExampleGroup。在传递给 describe
的代码块中,您可以使用 it
方法声明示例。
在幕后,一个示例组是一个类,在其中传递给 describe
的代码块被评估。传递给 it
的代码块在该类的实例上下文中被评估。
嵌套组
您也可以使用 describe
或 context
方法声明嵌套组。
RSpec.describe Order do
context "with no items" do
it "behaves one way" do
# ...
end
end
context "with one item" do
it "behaves another way" do
# ...
end
end
end
嵌套组是外部示例组类的子类,为其提供您期望的继承语义。
别名
您可以使用 describe
或 context
声明示例组。对于顶级示例组,describe
和 context
可从 RSpec
中使用。为了向后兼容,它们也可以从 main
对象和 Module
中使用,除非您禁用了猴子补丁。
您可以在一个组中使用 it
、specify
或 example
中的任何一个声明示例。
共享示例和上下文
使用 shared_examples
声明一个共享示例组,然后使用 include_examples
将其包含在任何组中。
RSpec.shared_examples "collections" do |collection_class|
it "is empty when first created" do
expect(collection_class.new).to be_empty
end
end
RSpec.describe Array do
include_examples "collections", Array
end
RSpec.describe Hash do
include_examples "collections", Hash
end
几乎任何可以在示例组中声明的都可以声明在共享示例组中。这包括 before
、after
和 around
钩子、let
声明以及嵌套组/上下文。
您也可以使用 shared_context
和 include_context
这两个名称。它们与 shared_examples
和 include_examples
几乎相同,只是在共享钩子、let
声明、辅助方法等时提供更准确的命名,但没有示例。
如果您希望在整个 RSpec 套件中重用共享示例或上下文,您可以将它们定义在独立的 *.rb 文件中(例如,spec/support/shared_examples/definition.rb)。但是您必须手动 require
它们(除非您自己设置,否则不会自动加载 spec/support/ 目录)。
元数据
rspec-core 为每个示例和组存储一个元数据哈希,其中包含它们的描述、声明它们的位置等。此哈希为 rspec-core 的许多功能提供了支持,包括输出格式器(访问描述和位置)以及过滤 before 和 after 钩子。
尽管您可能永远不需要它,除非您正在编写扩展程序,但您可以从示例中以这种方式访问它
it "does something" do |example|
expect(example.[:description]).to eq("does something")
end
described_class
当一个类被传递给 describe
时,您可以使用 described_class
方法从示例中访问它,它是一个 example.metadata[:described_class]
的包装器。
RSpec.describe Widget do
example do
expect(described_class).to equal(Widget)
end
end
这在扩展程序或共享示例组中很有用,在这些组中,特定类是未知的。以上面的集合共享示例组为例,我们可以使用 described_class
稍微清理一下它
RSpec.shared_examples "collections" do
it "is empty when first created" do
expect(described_class.new).to be_empty
end
end
RSpec.describe Array do
include_examples "collections"
end
RSpec.describe Hash do
include_examples "collections"
end
关于范围的一句话
RSpec 有两个范围
- 示例组:示例组由
describe
或context
代码块定义,该代码块在加载规范文件时被热切地评估。该代码块在RSpec::Core::ExampleGroup
的子类或当您嵌套它们时父示例组的子类的上下文中被评估。 - 示例:示例(通常由
it
代码块定义)以及任何其他具有每个示例语义的代码块(例如before(:example)
钩子)在该示例所属的示例组类的实例上下文中被评估。示例不会在加载规范文件时执行;相反,RSpec 会等待运行所有示例,直到所有规范文件都被加载,此时它可以应用过滤、随机化等。
为了使这更加具体,请考虑以下代码片段
RSpec.describe "Using an array as a stack" do
def build_stack
[]
end
before(:example) do
@stack = build_stack
end
it 'is initially empty' do
expect(@stack).to be_empty
end
context "after an item has been pushed" do
before(:example) do
@stack.push :item
end
it 'allows the pushed item to be popped' do
expect(@stack.pop).to eq(:item)
end
end
end
在幕后,这大致等同于
class UsingAnArrayAsAStack < RSpec::Core::ExampleGroup
def build_stack
[]
end
def before_example_1
@stack = build_stack
end
def it_is_initially_empty
expect(@stack).to be_empty
end
class AfterAnItemHasBeenPushed < self
def before_example_2
@stack.push :item
end
def it_allows_the_pushed_item_to_be_popped
expect(@stack.pop).to eq(:item)
end
end
end
为了运行这些示例,RSpec 大致会执行以下操作
example_1 = UsingAnArrayAsAStack.new
example_1.before_example_1
example_1.it_is_initially_empty
example_2 = UsingAnArrayAsAStack::AfterAnItemHasBeenPushed.new
example_2.before_example_1
example_2.before_example_2
example_2.it_allows_the_pushed_item_to_be_popped
rspec
命令
当您安装 rspec-core gem 时,它会安装 rspec
可执行文件,您将使用它来运行 rspec。rspec
命令附带了许多有用的选项。运行 rspec --help
以查看完整的列表。
存储命令行选项 .rspec
您可以在项目根目录中的 .rspec
文件中存储命令行选项,rspec
命令会像您在命令行中输入一样读取它们。
入门
从您对系统期望的行为的简单示例开始。在您编写任何实现代码之前执行此操作。
# in spec/calculator_spec.rb
RSpec.describe Calculator do
describe '#add' do
it 'returns the sum of its arguments' do
expect(Calculator.new.add(1, 2)).to eq(3)
end
end
end
使用 rspec 命令运行它,并观察它失败
$ rspec spec/calculator_spec.rb
./spec/calculator_spec.rb:1: uninitialized constant Calculator
通过定义 Calculator
类的骨架来解决故障
# in lib/calculator.rb
class Calculator
def add(a, b)
end
end
确保在规范中要求实现文件
# in spec/calculator_spec.rb
# - RSpec adds ./lib to the $LOAD_PATH
require "calculator"
现在再次运行规范,并观察期望失败
$ rspec spec/calculator_spec.rb
F
Failures:
1) Calculator#add returns the sum of its arguments
Failure/Error: expect(Calculator.new.add(1, 2)).to eq(3)
expected: 3
got: nil
(compared using ==)
# ./spec/calculator_spec.rb:6:in `block (3 levels) in <top (required)>'
Finished in 0.00131 seconds (files took 0.10968 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/calculator_spec.rb:5 # Calculator#add returns the sum of its arguments
实现最简单的解决方案,通过将 Calculator#add
的定义更改为
def add(a, b)
a + b
end
现在再次运行规范,并观察它通过
$ rspec spec/calculator_spec.rb
.
Finished in 0.000315 seconds
1 example, 0 failures
使用 documentation
格式化程序查看生成的规范
$ rspec spec/calculator_spec.rb --format doc
Calculator
#add
returns the sum of its arguments
Finished in 0.000379 seconds
1 example, 0 failures
贡献
设置好环境后,您需要 cd 到您想在其中工作的任何库的工作目录。从那里您可以运行规范和黄瓜功能,并创建补丁。
注意:您不需要使用 rspec-dev 来处理特定的 RSpec 库。您可以将每个 RSpec 库视为一个独立的项目。