Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added 'application' configuration setting to set a Rack app to allow changing routes in controller specs #471

Closed
wants to merge 1 commit into from

5 participants

@szimek

Currently RSpec hardcodes Rails.application.routes in controller specs (https://github.com/rspec/rspec-rails/blob/master/lib/rspec/rails/example/controller_example_group.rb#L125) This makes it hard to run controller specs for Rails engines that provide their own route set.

Currently I need to set routes manually in before block in every test - I can't do it in RSpec config, because before block of ControllerExampleGroup runs after before block in RSpec config and overwrites @route variable.

With this new setting one can just do:

RSpec.configure do |config|
  config.application = MyApp::Engine
end

in engine's spec_helper.rb and it will properly set routing for controller specs.

Additionally it would be great if it could also do:

RSpec.configure do |config|
  config.include MyApp::Engine.routes.url_helpers
end

but it's not included in this commit.

If you think that the whole idea makes sense, I can add some more detailed tests, because right now I only added tests for the new application setting.

@justinko

You need to make the engine routes available in the Rails app routes. In your engine's config/routes.rb, "draw" your routes with Rails.application:

Rails.application.routes.draw do |map|
  resources :accounts
end

Rails.application.routes should now include them.

@justinko justinko closed this
@szimek

I don't really understand... In my case the whole point of separating my app into engines was to have separate route sets, so that I don't have to prefix named route helpers e.g. I can use dashboard_path instead of my_engine_dashboard_path inside my engine and it will work correctly even if other engines also have dashboard_path defined.

Currently my routes look like this (it's simplified - I've got 3 engines in my app - all mounted to "/", but with different constraints):

main app:

MyApp::Application.routes.draw do
  scope :constraints => Routing::Constraints::Subdomain.new do
    mount MyEngine::Engine => "/"
  end

  scope :constraints => Routing::Constraints::Something.new do
    mount MyOtherEngine::Engine => "/"
  end

  # Static pages
  get '/pages/*id' => 'pages#show', :as => :page
end

first engine:

MyEngine::Engine.routes.draw do
  resource :dashboard, :only => [:show]
end

second engine:

MyOtherEngine::Engine.routes.draw do
  resource :dashboard, :only => [:show]
end

and the app works fine. Request specs also work fine. However, in controller specs I'm getting routing errors, because RSpec hardcodes routes to Rails.application.routes and if you check these routes they do not include engine routes. If you try to generate URL e.g. for my_engine/dashboard#show using Rails.application.routes route set, it won't work. Thus, in controller specs for an engine (every engine has it's own spec_helper.rb file, so it's not a problem), I'd like to be able to switch used route set, because I'm only testing controllers from this particular engine, not from the whole Rails app.

@justinko

Change your engine routes to this:

Rails.application.routes.draw do
  resource :dashboard, :only => [:show]
end

Let me know if that works.

@szimek

It most likely is going to work, because it will add this route to Rails.application.routes route set that RSpec is using in controller specs.

The issue is that if you have 3 different engines and each of them defines the following route:

resource :dashboard, :only => [:show]

then it's impossible to use the solution you're suggesting, because each of these routes points to a different controller (from different engine), but generates the same URL.

Thus, each engine defines its own route set that is later mounted in Rails.application.routes route set like described in my previous comment.

@drogus

@justinko

I agree with @szimek on this one. Before rails 3.1, engine didn't have ability to have its own routes, so all of the changes were done as you proposed: by extending Rails.application.routes. Now it looks a bit different as engines can have routes that can be mounted into application routes like that:

Rails.application.routes.draw do
  mount Blog::Engine => "/blolololog"
end

In order to test those routes with controller's unit tests, you don't want to use Rails.application.routes as you will also need to include mount point, which is not ideal as this destroys the flexibility (you can't change mount poing to something else without changing tests).

The other problem with this is that mountable engine can't be run standalone, which means that it needs to be mounted in an application (which can be fake app only for testing or real app if you use engines to isolate parts of your app), so we also can't do something like: Rails.application = Blog::Engine.

In summary, this option is really needed if you want to unit test controllers of mountable engine.

@justinko justinko reopened this
@justinko

@drogus - alright I see what's going on now. Thanks for the thorough explanation.

@szimek

If you think that the whole idea makes sense, I can add some more detailed tests

Please do!

@danrasband

What needs to be done in order to get the pull request merged? I'm happy to help.

@szimek

@danrasband I remember that I couldn't figure out how to write a proper test for that feature. I also found out that ::Rails.application is used in few other places as well and I wasn't sure if it's ok to switch it everywhere or if it's enough to switch it just in this case.

Anyway the patch, as it is right now, was fixing the original issue for me.

@danrasband

I just created a new pull request for this with tests. I also dealt with ::Rails.application in all locations throughout the gem:

#539

@alindeman
Collaborator

Closing and focusing on #539. Thanks!

@alindeman alindeman closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
3  lib/rspec/rails/example/controller_example_group.rb
@@ -1,5 +1,6 @@
RSpec.configure do |config|
config.add_setting :infer_base_class_for_anonymous_controllers, :default => false
+ config.add_setting :application, :default => ::Rails.application
end
module RSpec::Rails
@@ -122,7 +123,7 @@ def method_missing(method, *args, &block)
metadata[:type] = :controller
before do
- @routes = ::Rails.application.routes
+ @routes = RSpec.configuration.application.routes
ActionController::Base.allow_forgery_protection = false
end
end
View
8 spec/rspec/rails/configuration_spec.rb
@@ -3,10 +3,12 @@
describe "configuration" do
before do
@orig_render_views = RSpec.configuration.render_views?
+ @orig_application = RSpec.configuration.application
end
after do
RSpec.configuration.render_views = @orig_render_views
+ RSpec.configuration.application = @orig_application
end
describe "#render_views?" do
@@ -23,4 +25,10 @@
RSpec.configuration.render_views?.should be_true
end
end
+
+ describe "#application" do
+ it "is Rails.application by default" do
+ RSpec.configuration.application.should eq(::Rails.application)
+ end
+ end
end
Something went wrong with that request. Please try again.