Skip to content

UrlGenerationError when testing mocked controllers that use url_for just to add params #2506

Open
@henrahmagix

Description

@henrahmagix

What Ruby, Rails and RSpec versions are you using?

Ruby version: 2.6.7
Rails version: 6.0.3.7
RSpec version:

RSpec 3.10
  - rspec-core 3.10.1
  - rspec-expectations 3.10.1
  - rspec-mocks 3.10.2
  - rspec-rails 5.0.1 (and 4.0.2 before updating)
  - rspec-support 3.10.2

Observed behaviour

Using redirect_to url_for(my_extra_param: 'a value') in a Concern, and then testing that concern with a controller test using controller(ActionController::Base.include(MyConcern)) and requesting an action defined in the test controller and drawn with routes.draw, always fails with ActionController::UrlGenerationError as it cannot find the test routes.

Expected behaviour

Test routes added with routes.draw are available and url_for succeeds to build a url of the current route.

Can you provide an example app?

EDIT: here's a repo that reproduces this issue: https://github.com/henrahmagix/rails-bug-app/tree/c887222dc542572c4453a2c0f987afeefd730065

I will leave the example originally posted here intact below.


Unfortunately rubygems is down right now, so I can't bundle install on a new app to double-check my setup shows the bug correctly. So here's the test file that I would add to rails new that shows it:

Click to expand

require 'rspec/rails'

RSpec.configure do |config|
  # rspec-expectations config goes here. You can use an alternate
  # assertion/expectation library such as wrong or the stdlib/minitest
  # assertions if you prefer.
  config.expect_with :rspec
  config.mock_with :rspec
  config.filter_rails_from_backtrace!
end

module MockConcern
  extend ActiveSupport::Concern

  included do
    before_action :redirect_if_requested
  end

  private

    def redirect_if_requested
      redirect_to url_for(redirected_from_concern: true) if params[:redirect_me] == 'yes'
    end
end

describe 'url_for bug in concern tests', type: :controller do
  controller(ActionController::Base.include(MockConcern)) do
    def test
      render plain: 'testing'
    end
  end

  before do
    routes.draw do
      get "test" => "anonymous#test"
    end
  end

  it 'succeeds when url_for is not called in the concern' do
    get :test, params: { redirect_me: 'no' }, session: nil
    expect(response).not_to be_redirect
  end

  it 'fails when url_for is called in the concern' do
    get :test, params: { redirect_me: 'yes' }, session: nil
    expect(response).to be_redirect
  end
end

class MockController < ActionController::Base
  def in_real
    redirect_to url_for(extra_param: 'in_real')
  end
end

describe 'url_for bug in controller tests', type: :controller do
  controller(MockController) do
    def test
      render plain: 'testing'
    end

    def in_mock
      redirect_to url_for(extra_param: 'in_mock')
    end
  end

  before do
    routes.draw do
      get "test" => "mock#test"
      get "in_real" => "mock#in_real"
      get "in_mock" => "mock#in_mock"
    end
  end

  it 'succeeds when url_for is not called' do
    get :test, params: nil, session: nil
    expect(response).not_to be_redirect
    expect(response.body).to eql('testing')
  end

  it 'fails when url_for is called in the real controller' do
    get :in_real, params: nil, session: nil
    expect(response).to be_redirect
  end

  it 'fails when url_for is called in the mock controller' do
    get :in_mock, params: nil, session: nil
    expect(response).to be_redirect
  end
end

I will link to a rails new app that I've confirmed shows the same results once rubygems is back up =)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions