Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
writing Rails engine rspec controller tests

If you are trying to test controllers under a Rails engine, you might come across this error:


Failure/Error: get :index
ActionController::UrlGenerationError:
  No route matches {:action=>"index", :controller=>"engine_name/controller_name"}

Despite, setting up your routes, controller, and mount correctly, the tests cannot find your route. There are three possible solutions to this problem (the third is my favorite).

Solution 1:
You can specify a use_route param in each of your controller spec get/post/put/delete calls. The use_route param goes inside of HTTP params and needs a value that matches your engine name in snake case. So if you want to do a GET to the index action of a PostsController located in a MyBlog engine, then your test would look like:


  it "does something..." do
    get :index, use_route: :my_blog
    ...
  end

The down fall to this solution is that you have to specify this use_route param in EVERY call to get/post/put/delete. Another better option is…

Solution 2:
You can specify the routes to use in the outer most describe block of your controller test. You do this by calling the routes { EngineName::Engine.routes }, here’s an example:


describe MyBlog::PostsController do
  routes { MyBlog::Engine.routes }

  describe "GET index" do
    it "does something..." do
      get :index
      ...
    end
  end
end

This solution requires an additional line of code in each of your controller spec files. This is much better than Solution 1. But if you’re like me and don’t want any extra code in your controller tests, I suggest trying…

Solution 3:
You can monkey patch the get/post/put/delete method calls that your rspec tests use so that they always pass a use_route param. I would recommend doing this by creating a file under your engine’s spec/support directory and putting this in it:


module EngineControllerTestMonkeyPatch
  def get(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "GET")
  end

  # Executes a request simulating POST HTTP method and set/volley the response
  def post(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "POST")
  end

  # Executes a request simulating PUT HTTP method and set/volley the response
  def put(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "PUT")
  end

  # Executes a request simulating DELETE HTTP method and set/volley the response
  def delete(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "DELETE")
  end

  private

  def process_action(action, parameters = nil, session = nil, flash = nil, method = "GET")
    parameters ||= {}
    process(action, method, parameters.merge!(:use_route => :my_engine_name), session, flash, )
  end
end

The only change you need to make is on the last line of code, change my_engine_name to the name of the engine your testing in snake case. Next include this module when you run your controller tests by adding this line to the spec_helper.rb of your engine:


RSpec.configure do |config|
  ...
  config.include EngineControllerTestMonkeyPatch, :type => :controller
  ...
end

Now you can test your controllers as you would normally (without any extra code)!

Big thank you to Jared Fraser for commenting below and providing with Solution 2 and Ryan Bigg for Solution 3.

Reference: http://stackoverflow.com/questions/5200654/how-do-i-write-a-rails-3-1-engine-controller-test-in-rspec

Comments
  1. Jared Fraser says:

    You can put “routes { YourEngine::Engine.routes }” in your describe block to solve the same problem in most cases.

  2. Ben Smith says:

    Thanks Jared! I’ll update my post to include this option.

  3. Michal W says:

    Hey, you have an error in the `process` method call, it should be:

    “`ruby

    process(action, parameters.merge!(:use_route => :my_engine_name), session, flash, method)

    “`

  4. Jeff Dean says:

    Another simple way to accomplish this is to just set the routes in a before block for all controller specs like:

    “`
    RSpec::Rails::ControllerExampleGroup.included do
    before do
    self.routes = Provider::Engine.routes
    end
    end
    “`

    The `self.routes` call is a private API, so this might break in future versions, but it works in rspec 2.14.1.

  5. Pedro says:

    “routes { MyEngine::Engine.routes }”
    this solution is unsupported on rails 3.0

  6. Dwi Wahyudi says:

    solution 2 doesn’t work
    routes { MyBlog::Engine.routes }

    solution 1 works

    what happens? :(

  7. filippos says:

    I think the second solution is the best, not the 3rd. I avoid any monkey patching. 1 line per test which clearly specifies what it does ( instead of the monkey patch) I think is better.

    Nevertheless great article.

  8. Rob Jewell says:

    Thank you for demonstrating the, use_route, param. This helped a lot!

  9. Richard says:

    Thank you for this pattern. I have included it in an engine example at the git hub link below.

    This engine includes RSpec, Shoulda and Factory Girl. I put this together after working through the particulars of making these all work in an engine appropriately. As I run across nuances I add them to this base repository for reference.

    https://github.com/nugenius/rails-engine-base

  10. Ryan Francis says:

    Thanks! Just ran into this problem and didn’t expect to find a blog post on Rails Engine controller testing. I used solution 2 since my engine only has one controller…worked like a charm.

  11. Amol Pujari says:

    All these solutions are working in my case. Thank for sharing them. But I wonder that I do have multiple apps, and all are using single engine. One app does not need this solution but other needs it, I did not find any differences between them too w.r.t. setting of rspec config etc.

  12. Matt Huggins says:

    Here’s a variation of #2 that doesn’t require manually including the `routes` line in every controller test: https://gist.github.com/mhuggins/d91a752684e552c05321

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *