Search code examples
ruby-on-railscapybararspec-rails

expected ActionController::RoutingError but was not given a block in rails app rspec


I try to test my RoR application. When i test to cause ActionController::RoutingError in my controller i get following errors: Got 1 failure and 1 other error:

 2.1) Failure/Error: expect(visit meters_path).to raise_error(ActionController::RoutingError)
        expected ActionController::RoutingError but was not given a block
      # ./spec/integration/meter_behavior_spec.rb:70:in `block (2 levels) in <top (required)>'

 2.2) Failure/Error: raise ActionController::RoutingError.new('Not Found')

      ActionController::RoutingError:
        Not Found

What is the reason? It is my list of testing gems:

group :development, :test do
  gem 'factory_girl_rails'
  gem 'faker'
  gem 'rspec-rails'
end
group :test do
  gem 'capybara'
  gem 'database_cleaner'
  gem 'launchy'
  gem 'selenium-webdriver', '2.53.4'
  gem 'capybara-webkit'
  gem 'shoulda-matchers', '~> 3.1'
end

my spec file

meter_behavior_spec.rb

  scenario 'a visitor cannot to view meters pages', driver: :webkit do
    user = FactoryGirl.create(:client1)
    user.meters.create(attributes_for(:meter))          
    expect(visit meters_path).to raise_error(ActionController::RoutingError)
  end

meters_controller.rb:

 before_action only: :index do
    access_allowed_for(['Client'])
  end
  # GET /meters
  # GET /meters.json
  def index
    respond_to do |format|
      format.json { render json: MetersDatatable.new(view_context, current_user) }
      format.html
    end
  end

application_controller.rb:

  def access_allowed_for(statuses_ary=[])
    statuses_ary << 'Admin'
    if !user_signed_in? || statuses_ary.exclude?(current_user.try(:status))
      raise ActionController::RoutingError.new('Not Found')
    end
  end

Solution

  • The first issue as pointed out in Jean-Christophe's answer is that the raise_error matcher needs a block. The second issue is you're testing something that would really be better tested in a controller test (not Capybara driven) instead of a feature test.

    If you insist on testing this in a feature test then there's a few things you need to verify

    1. Make sure you don't have any gems in the test environment that are catching the errors and producing nice/detailed error pages. This would include gems like web_console, better_errors, etc. Those gems should be only in the development environment.

    2. Make sure you have set Capybara.raise_server_errors = true in your test config

    3. You may need to add a second Capybara statement inside the block. This is because Capybara runs the app in a separate thread from the tests and the visit call may occur asynchronously (depending on the driver you use). That means the error may not have actually been raised when the visit call returns. By adding a second call to a capybara method it will wait a little bit for the visit to finish and then detect that an error was raised in the server/app thread and re-raise that error in the test thread.

       expect{
         visit meters_path
         page.has_text? 'Random text' # only here to give time for app to raise error
       }.to raise_error(ActionController::RoutingError)