Logo: Relish

  1. Sign up
  2. Sign in

Project: RSpec Rails 3.5

Directory Structure

Specs are usually placed in a canonical directory structure that describes
their purpose:

  • Model specs reside in the spec/models directory
  • Controller specs reside in the spec/controllers directory
  • Request specs reside in the spec/requests directory. The directory can also be named integration or api.
  • Feature specs reside in the spec/features directory
  • View specs reside in the spec/views directory
  • Helper specs reside in the spec/helpers directory
  • Mailer specs reside in the spec/mailers directory
  • Routing specs reside in the spec/routing directory

Application developers are free to use a different directory structure. In
order to include the correct rspec-rails support functions, the specs need
to have the appropriate corresponding metadata :type value:

  • Model specs: type: :model
  • Controller specs: type: :controller
  • Request specs: type: :request
  • Feature specs: type: :feature
  • View specs: type: :view
  • Helper specs: type: :helper
  • Mailer specs: type: :mailer
  • Routing specs: type: :routing

For example, say the spec for the ThingsController is located in
spec/legacy/things_controller_spec.rb. Simply tag the spec's
RSpec.describe block with the type: :controller metadata:

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

Note: Standard RSpec specs do not require any additional metadata by
default.

Check out the rspec-core documentation on using metadata for more details.

Automatically Adding Metadata

RSpec versions before 3.0.0 automatically added metadata to specs based on
their location on the filesystem. This was both confusing to new users and not
desirable for some veteran users.

In RSpec 3, this behavior must be explicitly enabled:

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

Since this assumed behavior is so prevalent in tutorials, the default
configuration generated by rails generate rspec:install enables this.

If you follow the above listed canonical directory structure and have
configured infer_spec_type_from_file_location!, RSpec will automatically
include the correct support functions for each type.

If you want to set metadata for a custom directory that doesn't follow fit the canonical structure above, you can do the following:

​# 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

Tips on Spec Location

It is suggested that the spec/ directory structure generally mirror both
app/ and lib/. This makes it easy to locate corresponding code and spec
files.

Example:

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
├── enviroment_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
Scenarios
Standard Rails specs must specify the `:type` metadata
Given
a file named "spec/functional/widgets_controller_spec.rb" with:
require "rails_helper"

RSpec.describe WidgetsController, :type => :controller do
  it "responds successfully" do
    get :index
    expect(response.status).to eq(200)
  end
end
When
I run rspec spec
Then
the example should pass
Given
a file named "spec/ledger/entry_spec.rb" with:
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
When
I run rspec spec
Then
the example should pass
Inferring spec type from the file location adds the appropriate metadata
Given
a file named "spec/controllers/widgets_controller_spec.rb" with:
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
When
I run rspec spec
Then
the example should pass
Specs in canonical directories can override their inferred types
Given
a file named "spec/routing/duckduck_routing_spec.rb" with:
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
When
I run rspec spec
Then
the example should pass

Last published 6 months ago by Jon Rowe.