diff --git a/features/.nav b/features/.nav index c24099f844..096b300fdd 100644 --- a/features/.nav +++ b/features/.nav @@ -4,6 +4,7 @@ - Changelog.md - Upgrade.md - RailsVersions.md (Rails versions) +- directory_structure.feature - model_specs: - errors_on.feature - records.feature diff --git a/features/directory_structure.feature b/features/directory_structure.feature new file mode 100644 index 0000000000..9cdfbbdb5b --- /dev/null +++ b/features/directory_structure.feature @@ -0,0 +1,71 @@ +Feature: 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 + * 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 + + If you follow this directory structure, RSpec will automatically include the + correct test support functions for each type of test. + + Application developers are free to use a different directory structure, but + will need to specify the types manually by adding a `:type` metadata key (for + example, `describe WidgetsController, :type => :controller`) + + Scenario: Specs in the `spec/controllers` directory automatically tagged as controller specs + Given a file named "spec/controllers/widgets_controller_spec.rb" with: + """ruby + require "spec_helper" + + 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 + + Scenario: Specs in other directories must have their types specified manually + Given a file named "spec/functional/widgets_controller_spec.rb" with: + """ruby + require "spec_helper" + + 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 + + Scenario: Specs in canonical directories can override their types + Given a file named "spec/routing/duckduck_routing_spec.rb" with: + """ruby + require "spec_helper" + + Rails.application.routes.draw do + get "/example" => redirect("http://example.com") + end + + # Due to limitations in the Rails routing test framework, routes that + # perform redirects must actually be tested via request specs + 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 diff --git a/lib/rspec/rails/example.rb b/lib/rspec/rails/example.rb index 83d85afb93..d0213d209b 100644 --- a/lib/rspec/rails/example.rb +++ b/lib/rspec/rails/example.rb @@ -13,30 +13,53 @@ def c.escaped_path(*parts) Regexp.compile(parts.join('[\\\/]') + '[\\\/]') end - c.include RSpec::Rails::ControllerExampleGroup, :type => :controller, :example_group => { - :file_path => c.escaped_path(%w[spec controllers]) - } - c.include RSpec::Rails::HelperExampleGroup, :type => :helper, :example_group => { - :file_path => c.escaped_path(%w[spec helpers]) - } - if defined?(RSpec::Rails::MailerExampleGroup) - c.include RSpec::Rails::MailerExampleGroup, :type => :mailer, :example_group => { - :file_path => c.escaped_path(%w[spec mailers]) + c.include RSpec::Rails::ControllerExampleGroup, + :type => :controller, + :example_group => lambda { |example_group, metadata| + metadata[:type].nil? && c.escaped_path(%w[spec controllers]) =~ example_group[:file_path] + } + + c.include RSpec::Rails::HelperExampleGroup, + :type => :helper, + :example_group => lambda { |example_group, metadata| + metadata[:type].nil? && c.escaped_path(%w[spec helpers]) =~ example_group[:file_path] } + + if defined?(RSpec::Rails::MailerExampleGroup) + c.include RSpec::Rails::MailerExampleGroup, + :type => :mailer, + :example_group => lambda { |example_group, metadata| + metadata[:type].nil? && c.escaped_path(%w[spec mailers]) =~ example_group[:file_path] + } end - c.include RSpec::Rails::ModelExampleGroup, :type => :model, :example_group => { - :file_path => c.escaped_path(%w[spec models]) - } - c.include RSpec::Rails::RequestExampleGroup, :type => :request, :example_group => { - :file_path => c.escaped_path(%w[spec (requests|integration|api)]) - } - c.include RSpec::Rails::RoutingExampleGroup, :type => :routing, :example_group => { - :file_path => c.escaped_path(%w[spec routing]) - } - c.include RSpec::Rails::ViewExampleGroup, :type => :view, :example_group => { - :file_path => c.escaped_path(%w[spec views]) - } - c.include RSpec::Rails::FeatureExampleGroup, :type => :feature, :example_group => { - :file_path => c.escaped_path(%w[spec features]) - } + + c.include RSpec::Rails::ModelExampleGroup, + :type => :model, + :example_group => lambda { |example_group, metadata| + metadata[:type].nil? && c.escaped_path(%w[spec models]) =~ example_group[:file_path] + } + + c.include RSpec::Rails::RequestExampleGroup, + :type => :request, + :example_group => lambda { |example_group, metadata| + metadata[:type].nil? && c.escaped_path(%w[spec (requests|integration|api)]) =~ example_group[:file_path] + } + + c.include RSpec::Rails::RoutingExampleGroup, + :type => :routing, + :example_group => lambda { |example_group, metadata| + metadata[:type].nil? && c.escaped_path(%w[spec routing]) =~ example_group[:file_path] + } + + c.include RSpec::Rails::ViewExampleGroup, + :type => :view, + :example_group => lambda { |example_group, metadata| + metadata[:type].nil? && c.escaped_path(%w[spec views]) =~ example_group[:file_path] + } + + c.include RSpec::Rails::FeatureExampleGroup, + :type => :feature, + :example_group => lambda { |example_group, metadata| + metadata[:type].nil? && c.escaped_path(%w[spec features]) =~ example_group[:file_path] + } end