Search code examples
ruby-on-railsruby-on-rails-3rspecrspec-rails

Rails controller rspecs fail with RoutingError for spec/controllers but pass for individual tests


My controller specs are passing when I run them one by one but are all failing when I run them together as controllers.

$rspec spec/controllers

22) SubjectsController GET index assigns all subjects as @subjects
     Failure/Error: get :index, {}
     ActionController::RoutingError:
       No route matches {:controller=>"subjects"}
     # ./spec/controllers/subjects_controller_spec.rb:13:in `block (3 levels) in <top (required)>'

My guess is it must be something to do with database cleaner but I'm not sure best way to troubleshoot routing errors in controller specs.

Gemfile

gem 'rails', '3.2.13'
gem 'mysql2', '0.3.13'
gem 'devise', '2.2.3'
...
...
group :development, :test do
  gem 'rspec-rails', '~> 2.99'
  gem 'webrick',    '~> 1.3'
  gem 'rails-erd', '~> 1.1'
  gem 'bullet', '~> 4.6'
  gem 'pry-rails', '~> 0.3'
  gem 'factory_girl_rails', '~> 4.4'
  gem 'faker', '~> 1.2'
end

group :test do
  gem 'cucumber-rails', :require => false
  gem 'capybara', '~> 2.2.1'
  gem 'database_cleaner', '~> 1.3.0'
  gem 'launchy', '~> 2.4.2'
  gem 'shoulda-matchers', '~> 2.6.1'
  gem 'vcr', '~> 2.9.2'
  gem 'webmock', '~>  1.13.0'
  gem 'zeus', '~> 0.13.3'
end

spec/spec_helper.rb

# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] = 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rake'
require 'sidekiq/testing'

Sidekiq::Logging.logger = nil


# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

RSpec.configure do |config|
  # ## Mock Framework
  #
  # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
  #
  # config.mock_with :mocha
  # config.mock_with :flexmock
  # config.mock_with :rr
  config.mock_with :rspec

  # Only run tests that have this filter, if it exists
  # config.filter_run :debug => true

  # Run all the tests when all the tests are filtered
  # config.run_all_when_everything_filtered = true

  config.treat_symbols_as_metadata_keys_with_true_values = true

  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  # config.fixture_path = "#{::Rails.root}/spec/fixtures"

  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = false

  # If true, the base class of anonymous controllers will be inferred
  # automatically. This will be the default behavior in future versions of
  # rspec-rails.
  config.infer_base_class_for_anonymous_controllers = false

  config.include FactoryGirl::Syntax::Methods
  config.include Devise::TestHelpers, :type => :controller
  config.extend ControllerMacros, :type => :controller

  OmniAuth.config.test_mode = true

  OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new( {
      provider: 'facebook',
      uid: '123545',
      info: {
            first_name: 'John',
            last_name: 'Doe',
            email: '[email protected]'
        },
      credentials: {
          token: "123456",
          expires_at: Time.now + 1.week
      },
      extra: {
          raw_info: {
              gender: 'male'
          }
      }
  })

  # silence rspec 3 deprecation warnings
  config.expose_current_running_example_as :example
  config.infer_spec_type_from_file_location!

  def login(user)
    @request.env["devise.mapping"] = Devise.mappings[:user]
    @user = user
    @user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
    allow(controller).to receive(:current_user).and_return(@user)
    sign_in @user
  end

  def logged_user
    @user
  end

end

spec/support/database_cleaner.rb

RSpec.configure do |config|

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
    load "#{Rails.root}/db/seeds.rb"
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, :js => true) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

end

spec/controllers/subjects_controller_spec.rb

require 'spec_helper'

describe SubjectsController, :type => :controller do

  before(:each) do
    login create(:admin)
  end

  describe "GET index" do
    it "assigns all subjects as @subjects" do
      subject = create(:subject)
      get :index, {}
      expect(assigns(:subjects)).to include(subject)
    end
  end

  describe "GET show" do
    it "assigns the requested subject as @subject" do
      subject = create(:subject)
      get :show, {:id => subject.to_param}
      expect(assigns(:subject)).to eq(subject)
    end
  end

  describe "GET new" do
    it "assigns a new subject as @subject" do
      get :new, {}
      expect(assigns(:subject)).to be_a_new(Subject)
    end
  end

  describe "GET edit" do
    it "assigns the requested subject as @subject" do
      subject = create(:subject)
      get :edit, {:id => subject.to_param}
      expect(assigns(:subject)).to eq(subject)
    end
  end

  describe "POST create" do
    describe "with valid params" do
      it "creates a new Subject" do
        expect {
          post :create, {:subject => attributes_for(:subject) }
        }.to change(Subject, :count).by(1)
      end

      it "assigns a newly created subject as @subject" do
        post :create, { :subject => attributes_for(:subject) }
        expect(assigns(:subject)).to be_a(Subject)
        expect(assigns(:subject)).to be_persisted
      end

      it "redirects to the created subject" do
        post :create, {:subject => attributes_for(:subject) }
        expect(response).to redirect_to(subjects_path)
      end
    end

    describe "with invalid params" do
      it "assigns a newly created but unsaved subject as @subject" do
        allow_any_instance_of(Subject).to receive(:save).and_return(false)
        post :create, {:subject => {}}
        expect(assigns(:subject)).to be_a_new(Subject)
      end

      it "re-renders the 'new' template" do
        allow_any_instance_of(Subject).to receive(:save).and_return(false)
        post :create, { :subject => {} }
        expect(response).to render_template("new")
      end
    end
  end

  describe "PUT update" do
    describe "with valid params" do
      it "updates the requested subject" do
        @subject = create(:subject, name: "Alice")
        put :update, id: @subject, subject: attributes_for(:subject, name: "Bob")
        @subject.reload
        @subject.name.should eq("Bob")
      end

      it "assigns the requested subject as @subject" do
        @subject = create(:subject)
        put :update, {:id => @subject, :subject => attributes_for(:subject) }
        expect(assigns(:subject)).to eq(@subject)
      end

      it "redirects to the subject" do
        @subject = create(:subject)
        put :update, {:id => @subject, :subject => attributes_for(:subject) }
        expect(response).to redirect_to(edit_subject_path)
      end
    end

    describe "with invalid params" do
      it "assigns the subject as @subject" do
        @subject = create(:subject)
        allow_any_instance_of(Subject).to receive(:save).and_return(false)
        put :update, {:id => @subject, :subject => {}}
        expect(assigns(:subject)).to eq(@subject)
      end

      it "re-renders the 'edit' template" do
        @subject = create(:subject)
        allow_any_instance_of(Subject).to receive(:save).and_return(false)
        put :update, {:id => @subject, :subject => {}}
        expect(response).to render_template("edit")
      end
    end
  end

  describe "DELETE destroy" do
    it "destroys the requested subject" do
      subject = create(:subject)
      expect {
        delete :destroy, {:id => subject.id}
      }.to change(Subject, :count).by(-1)
    end

    it "redirects to the subjects list" do
      subject = create(:subject)
      delete :destroy, {:id => subject.id }
      expect(response).to redirect_to(subjects_url)
    end
  end

end

rake routes

             suggested_subjects GET    /suggested_subjects(.:format)                             suggested_subjects#index
         make_preferred_subject PUT    /subjects/:id/make_preferred(.:format)                    subjects#make_preferred
                       subjects GET    /subjects(.:format)                                       subjects#index
                                POST   /subjects(.:format)                                       subjects#create
                    new_subject GET    /subjects/new(.:format)                                   subjects#new
                   edit_subject GET    /subjects/:id/edit(.:format)                              subjects#edit
                        subject GET    /subjects/:id(.:format)                                   subjects#show
                                PUT    /subjects/:id(.:format)                                   subjects#update

app/controllers/subjects_controller.rb

class SubjectsController < ApplicationController

  load_and_authorize_resource

  def index
    @subjects = Subject.filter(params)

    respond_to do |format|
      format.html
      format.json { render json: {total: @subjects.count, subjects: @subjects}.to_json }
    end
  end
....
....
end

Solution

  • Figured it out.

    The ActionController::RoutingError was a result of another controller spec which was calling:

    routes.draw 
    

    This call wiped out the routes for subsequent specs. The solution for now (thanks to:

    http://pivotallabs.com/adding-routes-for-tests-specs-with-rails-3/ ) was to add:

    after do
    
        # be sure to reload routes after the tests run, otherwise all your 
        # other controller specs will fail
        Rails.application.reload_routes!
    
    end
    

    Also helpful were:

    https://github.com/rspec/rspec-rails/issues/817

    https://github.com/rspec/rspec-rails/issues/636

    Hope this helps and good luck!