Article

Weirdness in assert_redirected_to

Posted  by Blake Watters.

PublicCategorized as Software Development and Near-Time.

Tagged with rails.

Earlier today I began having an infuriating problem coming from assert_redirected_to. The tests were failing an error like the following:


test_index_user_not_login(Admin::ConfigurationControllerTest) [./test/ functional/admin/configuration_controller_test.rb:20]: response is not a redirection to all of the options supplied (redirection is <{:action=>"login", :controller=>"user"}>), difference: <{}>

Hrm I said. Difference is {} ? Shouldn’t that mean that there is no difference? I hit the web and found some references to this sort of problem, mostly involving absolute controller paths. I don’t use absolute controller paths very often, but tried the fix anyway. No dice. So I rolled up my sleeves and set up some breakpoints and got to debugging. The problem was in the following portion of assert_redirected_to:


assert_block(msg) do if options.is_a?(Symbol) @response.redirected_to options else options.keys.all? do |k| if k :controller then options[k] ActionController::Routing.controller_relative_to(@response.redirected_to[k], @controller.class.controller_path) else options[k] (@response.redirected_to[k].respond_to?(:to_param) ? @response.redirected_to[k].to_param : @response.redirected_to[k] unless @response.redirected_to[k].nil?) end end end
end

The error was being raised in options.keys.all? block. After breakpoint hackery, I found that there was a mismatch between :id => 1 and :id => ‘1’. The Fixnum was being coerced to a string by the to_param method in this line:


else options[k] == (@response.redirected_to[k].respond_to?(:to_param) ? @response.redirected_to[k].to_param : @response.redirected_to[k] unless @response.redirected_to[k].nil?)

So I have added a modified version of assert_redirected_to to my test_helper.rb file that coerces both the redirect and asserted parameters via to_param before comparing them. This made my problem go away. So here’s the newly modified method:


def assert_redirected_to(options = {}, message=nil) clean_backtrace do assert_response(:redirect, message)

if options.is_a?(String)
    msg = build_message(message, "expected a redirect to <?>, found one to <?>", options, @response.redirect_url)
    url_regexp = %r{^(\w+://.?(/|$|\?))(.)$}
    eurl, epath, url, path = [options, @response.redirect_url].collect do |url|
      u, p = (url_regexp =~ url) ? [$1, $3] : nil, url == '/') ? p : '/' + p]
    end.flatten
end
end
assert_equal(eurl, url, msg) if eurl && url
  assert_equal(epath, path, msg) if epath && path 
else
  @response_diff = options.diff(@response.redirected_to) if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash)
  msg = build_message(message, "response is not a redirection to all of the options supplied (redirection is <?>)#{', difference: <?>' if @response_diff}", 
                      @response.redirected_to || @response.redirect_url, @response_diff)
  assert_block(msg) do
    if options.is_a?(Symbol)
      @response.redirected_to == options
    else
      options.keys.all? do |k|
        value = (options[k].respond_to?(:to_param) ? options[k].to_param : options[k])
        if k  :controller then value  ActionController::Routing.controller_relative_to(@response.redirected_to[k], @controller.class.controller_path)
        else value == (@response.redirected_to[k].respond_to?(:to_param) ? @response.redirected_to[k].to_param : @response.redirected_to[k] unless @response.redirected_to[k].nil?)
        end
      end
    end
  end
end

Arrow_down Hide comments

Powered by Near-TimeTerms of Services | Privacy Policy | Security Policy |