使用共享示例

共享示例允许您描述类或模块的行为。声明时,共享组的内容将被存储。它只在另一个示例组的上下文中实现,该示例组提供了共享组运行所需的任何上下文。

共享组使用以下任何一种方法包含在另一个组中:

  include_examples "name"      # include the examples in the current context
  it_behaves_like "name"       # include the examples in a nested context
  it_should_behave_like "name" # include the examples in a nested context
  matching metadata            # include the examples in the current context

警告: 包含共享组的文件必须在使用它们的.rb 文件之前加载。虽然有一些约定来处理这个问题,但 RSpec 没有做任何特别的事情(比如自动加载)。这样做需要一个严格的文件命名约定,这会破坏现有的套件。

警告: 当您在当前上下文中多次包含参数化的示例时,您可能会覆盖以前的.rb 方法定义,并且最后一个声明获胜。所以,如果你有这种共享示例(或共享上下文)

  RSpec.shared_examples "some example" do |parameter|
    \# Same behavior is triggered also with either `def something; 'some value'; end`
    \# or `define_method(:something) { 'some value' }`
    let(:something) { parameter }
    it "uses the given parameter" do
      expect(something).to eq(parameter)
    end
  end

  RSpec.describe SomeClass do
    include_examples "some example", "parameter1"
    include_examples "some example", "parameter2"
  end

您实际上正在做的是(注意第一个示例将失败)

  RSpec.describe SomeClass do
    \# Reordered code for better understanding of what is happening
    let(:something) { "parameter1" }
    let(:something) { "parameter2" }

    it "uses the given parameter" do
      \# This example will fail because last let "wins"
      expect(something).to eq("parameter1")
    end

    it "uses the given parameter" do
      expect(something).to eq("parameter2")
    end
  end

为了防止这种细微的错误,如果您在同一个上下文中声明了多个具有相同名称的方法,将发出警告。如果您收到此警告,最简单的解决方案是用`it_behaves_like` 替换`include_examples`,这样可以避免方法覆盖,因为`it_behaves_like` 创建了嵌套上下文

约定


  1. 最简单的方法是显式地从使用共享示例的文件中要求带有共享示例的文件。请记住,RSpec 将`spec` 目录添加到`LOAD_PATH` 中,因此您可以说`require 'shared_examples_for_widgets'` 以要求`#{PROJECT_ROOT}/spec/shared_examples_for_widgets.rb` 中的文件。

  2. 一种约定是将包含共享示例的文件放在`spec/support/` 中,并从`spec/spec_helper.rb` 中要求该目录中的文件。

      Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
    

    从历史上看,这包含在`rspec-rails` 中生成的`spec/spec_helper.rb` 文件中。但是,为了降低测试套件的启动时间,最好不要自动要求目录中的所有文件。当只运行一个规范文件时,加载不必要的依赖项或执行不必要的设置会对第一个示例运行之前需要多长时间产生显著影响。

  3. 当所有包含共享组的组都位于同一个文件中时,只需在该文件中声明共享组即可。

在一个文件中包含在两个组中的共享示例组

假设 一个名为“collection_spec.rb”的文件包含

require "set"

RSpec.shared_examples "a collection" do
  let(:collection) { described_class.new([7, 2, 4]) }

  context "initialized with 3 items" do
    it "says it has three items" do
      expect(collection.size).to eq(3)
    end
  end

  describe "#include?" do
    context "with an item that is in the collection" do
      it "returns true" do
        expect(collection.include?(7)).to be(true)
      end
    end

    context "with an item that is not in the collection" do
      it "returns false" do
        expect(collection.include?(9)).to be(false)
      end
    end
  end
end

RSpec.describe Array do
  it_behaves_like "a collection"
end

RSpec.describe Set do
  it_behaves_like "a collection"
end

我运行`rspec collection_spec.rb --format documentation` 时

那么 示例应该全部通过

并且 输出应该包含

Array
  behaves like a collection
    initialized with 3 items
      says it has three items
    #include?
      with an item that is in the collection
        returns true
      with an item that is not in the collection
        returns false

Set
  behaves like a collection
    initialized with 3 items
      says it has three items
    #include?
      with an item that is in the collection
        returns true
      with an item that is not in the collection
        returns false

使用块为共享组提供上下文

假设 一个名为“shared_example_group_spec.rb”的文件包含

require "set"

RSpec.shared_examples "a collection object" do
  describe "<<" do
    it "adds objects to the end of the collection" do
      collection << 1
      collection << 2
      expect(collection.to_a).to match_array([1, 2])
    end
  end
end

RSpec.describe Array do
  it_behaves_like "a collection object" do
    let(:collection) { Array.new }
  end
end

RSpec.describe Set do
  it_behaves_like "a collection object" do
    let(:collection) { Set.new }
  end
end

我运行`rspec shared_example_group_spec.rb --format documentation` 时

那么 示例应该全部通过

并且 输出应该包含

Array
  behaves like a collection object
    <<
      adds objects to the end of the collection

Set
  behaves like a collection object
    <<
      adds objects to the end of the collection

将参数传递给共享示例组

假设 一个名为“shared_example_group_params_spec.rb”的文件包含

RSpec.shared_examples "a measurable object" do |measurement, measurement_methods|
  measurement_methods.each do |measurement_method|
    it "should return #{measurement} from ##{measurement_method}" do
      expect(subject.send(measurement_method)).to eq(measurement)
    end
  end
end

RSpec.describe Array, "with 3 items" do
  subject { [1, 2, 3] }
  it_should_behave_like "a measurable object", 3, [:size, :length]
end

RSpec.describe String, "of 6 characters" do
  subject { "FooBar" }
  it_should_behave_like "a measurable object", 6, [:size, :length]
end

我运行`rspec shared_example_group_params_spec.rb --format documentation` 时

那么 示例应该全部通过

并且 输出应该包含

Array with 3 items
  it should behave like a measurable object
    should return 3 from #size
    should return 3 from #length

String of 6 characters
  it should behave like a measurable object
    should return 6 from #size
    should return 6 from #length

将`it_should_behave_like` 设为`it_has_behavior` 的别名

假设 一个名为“shared_example_group_spec.rb”的文件包含

RSpec.configure do |c|
  c.alias_it_should_behave_like_to :it_has_behavior, 'has behavior:'
end

RSpec.shared_examples 'sortability' do
  it 'responds to <=>' do
    expect(sortable).to respond_to(:<=>)
  end
end

RSpec.describe String do
  it_has_behavior 'sortability' do
    let(:sortable) { 'sample string' }
  end
end

我运行`rspec shared_example_group_spec.rb --format documentation` 时

那么 示例应该全部通过

并且 输出应该包含

String
  has behavior: sortability
    responds to <=>

共享元数据自动包含共享示例组

假设 一个名为“shared_example_metadata_spec.rb”的文件包含

RSpec.shared_examples "shared stuff", :a => :b do
  it 'runs wherever the metadata is shared' do
  end
end

RSpec.describe String, :a => :b do
end

我运行`rspec shared_example_metadata_spec.rb` 时

那么 输出应该包含

1 example, 0 failures

共享示例可以按上下文嵌套

假设 一个名为“context_specific_examples_spec.rb”的文件包含

RSpec.describe "shared examples" do
  context "per context" do

    shared_examples "shared examples are nestable" do
      specify { expect(true).to eq true }
    end

    it_behaves_like "shared examples are nestable"
  end
end

我运行`rspec context_specific_examples_spec.rb` 时

那么 输出应该包含

1 example, 0 failures

共享示例可以从子级上下文访问

假设 一个名为“context_specific_examples_spec.rb”的文件包含

RSpec.describe "shared examples" do
  shared_examples "shared examples are nestable" do
    specify { expect(true).to eq true }
  end

  context "per context" do
    it_behaves_like "shared examples are nestable"
  end
end

我运行`rspec context_specific_examples_spec.rb` 时

那么 输出应该包含

1 example, 0 failures

并且 输出不应该包含

Accessing shared_examples defined across contexts is deprecated

共享示例在每个上下文中都是隔离的

假设 一个名为“isolated_shared_examples_spec.rb”的文件包含

RSpec.describe "shared examples" do
  context do
    shared_examples "shared examples are isolated" do
      specify { expect(true).to eq true }
    end
  end

  context do
    it_behaves_like "shared examples are isolated"
  end
end

我运行`rspec isolated_shared_examples_spec.rb` 时

那么 输出应该包含

Could not find shared examples \"shared examples are isolated\"