writing Rails engine rspec controller tests

September 14, 2013 Ben Smith

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

About the Author

Biography

Previous
A Wrap-Up of DevCon 5 2013
A Wrap-Up of DevCon 5 2013

HTML5 has long been one of the hottest topics in mobile technology circles. The concept of write once, depl...

Next
Tracker Ecosystem: Google Calendar integration
Tracker Ecosystem: Google Calendar integration

Cloud Integration Consultants have built a tool that will copy the finished stories from Pivotal Tracker to...