respond_to 匹配器

使用 respond_to 匹配器来指定对象接口的详细信息。 在最基本的形式中

  expect(obj).to respond_to(:foo) # pass if obj.respond_to?(:foo)

您可以在单个语句中指定对象响应多个消息,并将多个参数传递给匹配器

  expect(obj).to respond_to(:foo, :bar) # passes if obj.respond_to?(:foo) && obj.respond_to?(:bar)

如果方法接受的参数数量对您很重要,您也可以指定该数量

  expect(obj).to respond_to(:foo).with(1).argument
  expect(obj).to respond_to(:bar).with(2).arguments
  expect(obj).to respond_to(:baz).with(1..2).arguments
  expect(obj).to respond_to(:xyz).with_unlimited_arguments

如果您的 Ruby 版本支持关键字参数,您可以指定方法接受的关键字列表。

  expect(obj).to respond_to(:foo).with_keywords(:ichi, :ni)
  expect(obj).to respond_to(:bar).with(2).arguments.and_keywords(:san, :yon)
  expect(obj).to respond_to(:baz).with_arbitrary_keywords

请注意,此匹配器完全依赖于 #respond_to?。 如果对象通过 #method_missing 动态响应消息,但没有通过 #respond_to? 指示这一点,那么此匹配器将为您提供错误的结果。

基本用法

给定一个名为“respond_to_matcher_spec.rb”的文件,其中包含

RSpec.describe "a string" do
  it { is_expected.to respond_to(:length) }
  it { is_expected.to respond_to(:hash, :class, :to_s) }
  it { is_expected.not_to respond_to(:to_model) }
  it { is_expected.not_to respond_to(:compact, :flatten) }

  # deliberate failures
  it { is_expected.to respond_to(:to_model) }
  it { is_expected.to respond_to(:compact, :flatten) }
  it { is_expected.not_to respond_to(:length) }
  it { is_expected.not_to respond_to(:hash, :class, :to_s) }

  # mixed examples--String responds to :length but not :flatten
  # both specs should fail
  it { is_expected.to respond_to(:length, :flatten) }
  it { is_expected.not_to respond_to(:length, :flatten) }
end

我运行 rspec respond_to_matcher_spec.rb

那么输出应该包含所有这些

10 个示例,6 个失败
期望“一个字符串”响应 :to_model
期望“一个字符串”响应 :compact, :flatten
期望“一个字符串”不响应 :length
期望“一个字符串”不响应 :hash, :class, :to_s
期望“一个字符串”响应 :flatten
期望“一个字符串”不响应 :length

指定参数

给定一个名为“respond_to_matcher_argument_checking_spec.rb”的文件,其中包含

RSpec.describe 7 do
  it { is_expected.to respond_to(:zero?).with(0).arguments }
  it { is_expected.not_to respond_to(:zero?).with(1).argument }

  it { is_expected.to respond_to(:between?).with(2).arguments }
  it { is_expected.not_to respond_to(:between?).with(7).arguments }

  # deliberate failures
  it { is_expected.to respond_to(:zero?).with(1).argument }
  it { is_expected.not_to respond_to(:zero?).with(0).arguments }

  it { is_expected.to respond_to(:between?).with(7).arguments }
  it { is_expected.not_to respond_to(:between?).with(2).arguments }
end

我运行 rspec respond_to_matcher_argument_checking_spec.rb

那么输出应该包含所有这些

8 个示例,4 个失败
期望 7 响应 :zero? 具有 1 个参数
期望 7 不响应 :zero? 具有 0 个参数
期望 7 响应 :between? 具有 7 个参数
期望 7 不响应 :between? 具有 2 个参数

指定参数范围

给定一个名为“respond_to_matcher_argument_range_checking_spec.rb”的文件,其中包含

class MyClass
  def build(name, options = {})
  end

  def inspect
    'my_object'
  end
end

RSpec.describe MyClass do
  it { is_expected.to respond_to(:build).with(1..2).arguments }
  it { is_expected.not_to respond_to(:build).with(0..1).arguments }
  it { is_expected.not_to respond_to(:build).with(2..3).arguments }
  it { is_expected.not_to respond_to(:build).with(0..3).arguments }

  # deliberate failures
  it { is_expected.not_to respond_to(:build).with(1..2).arguments }
  it { is_expected.to respond_to(:build).with(0..1).arguments }
  it { is_expected.to respond_to(:build).with(2..3).arguments }
  it { is_expected.to respond_to(:build).with(0..3).arguments }
end

我运行 rspec respond_to_matcher_argument_range_checking_spec.rb

那么输出应该包含所有这些

8 个示例,4 个失败
期望 my_object 不响应 :build 具有 1..2 个参数
期望 my_object 响应 :build 具有 0..1 个参数
期望 my_object 响应 :build 具有 2..3 个参数
期望 my_object 响应 :build 具有 0..3 个参数

指定无限参数

给定一个名为“respond_to_matcher_unlimited_argument_checking_spec.rb”的文件,其中包含

class MyClass
  def greet(message = 'Hello', *people)
  end

  def hail(person)
  end

  def inspect
    'my_object'
  end
end

RSpec.describe MyClass do
  it { is_expected.to respond_to(:greet).with_unlimited_arguments }
  it { is_expected.to respond_to(:greet).with(1).argument.and_unlimited_arguments }
  it { is_expected.not_to respond_to(:hail).with_unlimited_arguments }
  it { is_expected.not_to respond_to(:hail).with(1).argument.and_unlimited_arguments }

  # deliberate failures
  it { is_expected.not_to respond_to(:greet).with_unlimited_arguments }
  it { is_expected.not_to respond_to(:greet).with(1).argument.and_unlimited_arguments }
  it { is_expected.to respond_to(:hail).with_unlimited_arguments }
  it { is_expected.to respond_to(:hail).with(1).argument.and_unlimited_arguments }
end

我运行 rspec respond_to_matcher_unlimited_argument_checking_spec.rb

那么输出应该包含所有这些

8 个示例,4 个失败
期望 my_object 不响应 :greet 具有无限参数
期望 my_object 不响应 :greet 具有 1 个参数和无限参数
期望 my_object 响应 :hail 具有无限参数
期望 my_object 响应 :hail 具有 1 个参数和无限参数

指定关键字

给定一个名为“respond_to_matcher_keyword_checking_spec.rb”的文件,其中包含

class MyClass
  def find(name = 'id', limit: 1_000, offset: 0)
    []
  end

  def inspect
    'my_object'
  end
end

RSpec.describe MyClass do
  it { is_expected.to respond_to(:find).with_keywords(:limit, :offset) }
  it { is_expected.to respond_to(:find).with(1).argument.and_keywords(:limit, :offset) }

  it { is_expected.not_to respond_to(:find).with_keywords(:limit, :offset, :page) }
  it { is_expected.not_to respond_to(:find).with(1).argument.and_keywords(:limit, :offset, :page) }

  # deliberate failures
  it { is_expected.to respond_to(:find).with_keywords(:limit, :offset, :page) }
  it { is_expected.to respond_to(:find).with(1).argument.and_keywords(:limit, :offset, :page) }

  it { is_expected.not_to respond_to(:find).with_keywords(:limit, :offset) }
  it { is_expected.not_to respond_to(:find).with(1).argument.and_keywords(:limit, :offset) }
end

我运行 rspec respond_to_matcher_keyword_checking_spec.rb

那么输出应该包含所有这些

8 个示例,4 个失败
期望 my_object 响应 :find 具有关键字 :limit, :offset 和 :page
期望 my_object 响应 :find 具有 1 个参数和关键字 :limit, :offset 和 :page
期望 my_object 不响应 :find 具有关键字 :limit 和 :offset
期望 my_object 不响应 :find 具有 1 个参数和关键字 :limit 和 :offset

指定任何关键字

给定一个名为“respond_to_matcher_any_keywords_checking_spec.rb”的文件,其中包含

class MyClass
  def build(name: 'object', **opts)
  end

  def create(name: 'object', type: String)
  end

  def inspect
    'my_object'
  end
end

RSpec.describe MyClass do
  it { is_expected.to respond_to(:build).with_any_keywords }
  it { is_expected.to respond_to(:build).with_keywords(:name).and_any_keywords }
  it { is_expected.not_to respond_to(:create).with_any_keywords }
  it { is_expected.not_to respond_to(:create).with_keywords(:name).and_any_keywords }

  # deliberate failures
  it { is_expected.not_to respond_to(:build).with_any_keywords }
  it { is_expected.not_to respond_to(:build).with_keywords(:name).and_any_keywords }
  it { is_expected.to respond_to(:create).with_any_keywords }
  it { is_expected.to respond_to(:create).with_keywords(:name).and_any_keywords }
end

我运行 rspec respond_to_matcher_any_keywords_checking_spec.rb

那么输出应该包含所有这些

8 个示例,4 个失败
期望 my_object 不响应 :build 具有任何关键字
期望 my_object 不响应 :build 具有关键字 :name 和任何关键字
期望 my_object 响应 :create 具有任何关键字
期望 my_object 响应 :create 具有关键字 :name 和任何关键字

指定必需关键字

给定一个名为“respond_to_matcher_required_keyword_checking_spec.rb”的文件,其中包含

class MyClass
  def plant(seed:, fertilizer: nil, water: 'daily')
    []
  end

  def inspect
    'my_object'
  end
end

RSpec.describe MyClass do
  it { is_expected.to respond_to(:plant).with_keywords(:seed) }
  it { is_expected.to respond_to(:plant).with_keywords(:seed, :fertilizer, :water) }
  it { is_expected.not_to respond_to(:plant).with_keywords(:fertilizer, :water) }

  # deliberate failures
  it { is_expected.not_to respond_to(:plant).with_keywords(:seed) }
  it { is_expected.not_to respond_to(:plant).with_keywords(:seed, :fertilizer, :water) }
  it { is_expected.to respond_to(:plant).with_keywords(:fertilizer, :water) }
end

我运行 rspec respond_to_matcher_required_keyword_checking_spec.rb

那么输出应该包含所有这些

6 个示例,3 个失败
期望 my_object 不响应 :plant 具有关键字 :seed
期望 my_object 不响应 :plant 具有关键字 :seed, :fertilizer 和 :water
期望 my_object 响应 :plant 具有关键字 :fertilizer 和 :water