目录结构

规范通常放置在描述其用途的规范目录结构中

应用程序开发人员可以自由地使用不同的目录结构。为了包含正确的 rspec-rails 支持函数,规范需要具有相应的元数据 :type

例如,假设 ThingsController 的规范位于 spec/legacy/things_controller_spec.rb 中。只需在规范的 RSpec.describe 块中标记 type: :controller 元数据即可

  # spec/legacy/things_controller_spec.rb
  RSpec.describe ThingsController, type: :controller do
    describe "GET index" do
      # Examples
    end
  end

注意:默认情况下,标准 RSpec 规范不需要任何其他元数据。

查看 rspec-core 文档上的 使用元数据 以获取更多详细信息。

自动添加元数据

3.0.0 之前的 RSpec 版本会根据规范在文件系统中的位置自动添加元数据。这对新用户来说既令人困惑,对一些资深用户来说也不理想。

必须明确启用此行为

  # spec/rails_helper.rb
  RSpec.configure do |config|
    config.infer_spec_type_from_file_location!
  end

由于这种假设的行为在教程中非常普遍,rails generate rspec:install 生成的默认配置启用了此行为。

如果您遵循上述列出的规范目录结构并已配置 infer_spec_type_from_file_location!,RSpec 将自动包含每个类型的正确支持函数。

如果您想为不符合上述规范结构的自定义目录设置元数据,可以执行以下操作

  # set `:type` for serializers directory
  RSpec.configure do |config|
    config.define_derived_metadata(:file_path => Regexp.new('/spec/serializers/')) do |metadata|
      metadata[:type] = :serializer
    end
  end

关于规范位置的提示

建议 spec/ 目录结构大体上镜像 app/lib/。这样可以轻松找到相应的代码和规范文件。

示例

  app
  ├── controllers
  │   ├── application_controller.rb
  │   └── books_controller.rb
  ├── helpers
  │   ├── application_helper.rb
  │   └── books_helper.rb
  ├── models
  │   ├── author.rb
  │   └── book.rb
  └── views
      ├── books
      └── layouts
  lib
  ├── country_map.rb
  ├── development_mail_interceptor.rb
  ├── environment_mail_interceptor.rb
  └── tasks
      └── irc.rake
  spec
  ├── controllers
  │   └── books_controller_spec.rb
  ├── country_map_spec.rb
  ├── features
  │   └── tracking_book_delivery_spec.rb
  ├── helpers
  │   └── books_helper_spec.rb
  ├── models
  │   ├── author_spec.rb
  │   └── book_spec.rb
  ├── rails_helper.rb
  ├── requests
  │   └── books_spec.rb
  ├── routing
  │   └── books_routing_spec.rb
  ├── spec_helper.rb
  ├── tasks
  │   └── irc_spec.rb
  └── views
      └── books

标准 Rails 规范必须指定 :type 元数据

给定一个名为“spec/functional/widgets_controller_spec.rb”的文件,其中包含

require "rails_helper"

RSpec.describe WidgetsController, type: :controller do
  it "responds successfully" do
    get :index
    expect(response.status).to eq(200)
  end
end

我运行 rspec spec

那么该示例应该通过。

与 Rails 无关的规范默认情况下不需要 :type 元数据

给定一个名为“spec/ledger/entry_spec.rb”的文件,其中包含

require "spec_helper"

Entry = Struct.new(:description, :us_cents)

RSpec.describe Entry do
  it "has a description" do
    is_expected.to respond_to(:description)
  end
end

我运行 rspec spec

那么该示例应该通过。

从文件位置推断规范类型会添加相应的元数据

给定一个名为“spec/controllers/widgets_controller_spec.rb”的文件,其中包含

require "rails_helper"

RSpec.configure do |config|
  config.infer_spec_type_from_file_location!
end

RSpec.describe WidgetsController do
  it "responds successfully" do
    get :index
    expect(response.status).to eq(200)
  end
end

我运行 rspec spec

那么该示例应该通过。

规范目录中的规范可以覆盖其推断的类型

给定一个名为“spec/routing/duckduck_routing_spec.rb”的文件,其中包含

require "rails_helper"

Rails.application.routes.draw do
  get "/example" => redirect("http://example.com")
end

RSpec.configure do |config|
  config.infer_spec_type_from_file_location!
end

# Due to limitations in the Rails routing test framework, routes that
# perform redirects must actually be tested via request specs
RSpec.describe "/example", type: :request do
  it "redirects to example.com" do
    get "/example"
    expect(response).to redirect_to("http://example.com")
  end
end

我运行 rspec spec

那么该示例应该通过。