RSpec 期望 构建状态 代码气候

RSpec::Expectations 允许您在示例中表达对对象预期结果。

expect(.balance).to eq(Money.new(37.42, :USD))

安装

如果您想将 rspec-expectations 与 rspec 一起使用,只需安装 rspec gem,RubyGems 也会为您安装 rspec-expectations(以及 rspec-core 和 rspec-mocks)

gem install rspec

想要针对 main 分支运行?您还需要包含依赖的 RSpec 仓库。在您的 Gemfile 中添加以下内容

%w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
  gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main'
end

如果您想将 rspec-expectations 与其他工具一起使用,如 Test::Unit、Minitest 或 Cucumber,您可以直接安装它

gem install rspec-expectations

贡献

设置好环境后,您需要 cd 到您要工作的任何仓库的工作目录。从那里您可以运行规范和黄瓜功能,并进行补丁。

注意:您不需要使用 rspec-dev 来处理特定的 RSpec 仓库。您可以将每个 RSpec 仓库视为一个独立的项目。

基本用法

以下是一个使用 rspec-core 的示例

RSpec.describe Order do
  it "sums the prices of the items in 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

describeit 方法来自 rspec-core。OrderLineItemItemMoney 类来自您的代码。示例的最后一行表达了预期结果。如果 order.total == Money.new(5.55, :USD),则示例通过。如果不是,则会失败并显示类似以下的消息

  expected: #<Money @value=5.55 @currency=:USD>
       got: #<Money @value=1.11 @currency=:USD>

内置匹配器

等效性

expect(actual).to eq(expected)  # passes if actual == expected
expect(actual).to eql(expected) # passes if actual.eql?(expected)
expect(actual).not_to eql(not_expected) # passes if not(actual.eql?(expected))

注意:新的 expect 语法不再支持 == 匹配器。

身份

expect(actual).to be(expected)    # passes if actual.equal?(expected)
expect(actual).to equal(expected) # passes if actual.equal?(expected)

比较

expect(actual).to be >  expected
expect(actual).to be >= expected
expect(actual).to be <= expected
expect(actual).to be <  expected
expect(actual).to be_within(delta).of(expected)

正则表达式

expect(actual).to match(/expression/)

注意:新的 expect 语法不再支持 =~ 匹配器。

类型/类

expect(actual).to be_an_instance_of(expected) # passes if actual.class == expected
expect(actual).to be_a(expected)              # passes if actual.kind_of?(expected)
expect(actual).to be_an(expected)             # an alias for be_a
expect(actual).to be_a_kind_of(expected)      # another alias

真值

expect(actual).to be_truthy   # passes if actual is truthy (not nil or false)
expect(actual).to be true     # passes if actual == true
expect(actual).to be_falsy    # passes if actual is falsy (nil or false)
expect(actual).to be false    # passes if actual == false
expect(actual).to be_nil      # passes if actual is nil
expect(actual).to_not be_nil  # passes if actual is not nil

期望错误

expect { ... }.to raise_error
expect { ... }.to raise_error(ErrorClass)
expect { ... }.to raise_error("message")
expect { ... }.to raise_error(ErrorClass, "message")

期望抛出

expect { ... }.to throw_symbol
expect { ... }.to throw_symbol(:symbol)
expect { ... }.to throw_symbol(:symbol, 'value')

屈服

expect { |b| 5.tap(&b) }.to yield_control # passes regardless of yielded args

expect { |b| yield_if_true(true, &b) }.to yield_with_no_args # passes only if no args are yielded

expect { |b| 5.tap(&b) }.to yield_with_args(5)
expect { |b| 5.tap(&b) }.to yield_with_args(Integer)
expect { |b| "a string".tap(&b) }.to yield_with_args(/str/)
expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3)
expect { |b| { :a => 1, :b => 2 }.each(&b) }.to yield_successive_args([:a, 1], [:b, 2])

谓词匹配器

expect(actual).to be_xxx         # passes if actual.xxx?
expect(actual).to have_xxx(:arg) # passes if actual.has_xxx?(:arg)

范围 (Ruby >= 1.9 仅限)

expect(1..10).to cover(3)

集合成员资格

# exact order, entire collection
expect(actual).to eq(expected)
# exact order, partial collection (based on an exact position)
expect(actual).to start_with(expected)
expect(actual).to end_with(expected)
# any order, entire collection
expect(actual).to match_array(expected)
# You can also express this by passing the expected elements
# as individual arguments
expect(actual).to contain_exactly(expected_element1, expected_element2)
 # any order, partial collection
expect(actual).to include(expected)

示例

expect([1, 2, 3]).to eq([1, 2, 3])            # Order dependent equality check
expect([1, 2, 3]).to include(1)               # Exact ordering, partial collection matches
expect([1, 2, 3]).to include(2, 3)            #
expect([1, 2, 3]).to start_with(1)            # As above, but from the start of the collection
expect([1, 2, 3]).to start_with(1, 2)         #
expect([1, 2, 3]).to end_with(3)              # As above but from the end of the collection
expect([1, 2, 3]).to end_with(2, 3)           #
expect({:a => 'b'}).to include(:a => 'b')     # Matching within hashes
expect("this string").to include("is str")    # Matching within strings
expect("this string").to start_with("this")   #
expect("this string").to end_with("ring")     #
expect([1, 2, 3]).to contain_exactly(2, 3, 1) # Order independent matches
expect([1, 2, 3]).to match_array([3, 2, 1])   #

# Order dependent compound matchers
expect(
  [{:a => 'hash'},{:a => 'another'}]
).to match([a_hash_including(:a => 'hash'), a_hash_including(:a => 'another')])

should 语法

除了 expect 语法之外,rspec-expectations 继续支持 should 语法

actual.should eq expected
actual.should be > 3
[1, 2, 3].should_not include 4

参见 有关 should 语法及其用法的详细信息。

复合匹配器表达式

您还可以使用 andor 创建复合匹配器表达式

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

组合匹配器

许多内置匹配器旨在将匹配器作为参数,以便您可以灵活地仅指定对象或数据结构的基本方面。此外,所有内置匹配器都具有一个或多个别名,这些别名在用作另一个匹配器的参数时提供了更好的措辞。

示例

expect { k += 1.05 }.to change { k }.by( a_value_within(0.1).of(1.0) )
expect { s = "barn" }.to change { s }
  .from( a_string_matching(/foo/) )
  .to( a_string_matching(/bar/) )
expect(["barn", 2.45]).to contain_exactly(
  a_value_within(0.1).of(2.5),
  a_string_starting_with("bar")
)
expect(["barn", "food", 2.45]).to end_with(
  a_string_matching("foo"),
  a_value > 2
)
expect(["barn", 2.45]).to include( a_string_starting_with("bar") )
expect(:a => "food", :b => "good").to include(:a => a_string_matching(/foo/))
hash = {
  :a => {
    :b => ["foo", 5],
    :c => { :d => 2.05 }
  }
}
expect(hash).to match(
  :a => {
    :b => a_collection_containing_exactly(
      a_string_starting_with("f"),
      an_instance_of(Integer)
    ),
    :c => { :d => (a_value < 3) }
  }
)
expect { |probe|
  [1, 2, 3].each(&probe)
}.to yield_successive_args( a_value < 2, 2, a_value > 2 )

在 rspec-core 之外的用法

即使您只想使用库的一部分,也始终需要加载 rspec/expectations

require 'rspec/expectations'

然后只需在任何类中包含 RSpec::Matchers

class MyClass
  include RSpec::Matchers
  def do_something(arg)
    expect(arg).to be > 0
    # do other stuff
  end
end

另请参阅