Let's say I have a FoosController
with a redirect_to_baz
method.
class FoosController < ApplicationController
def redirect_to_baz
redirect_to 'http://example.com/?foo=1&bar=2&baz=3'
end
end
I'm testing this with spec/controllers/foos_controller_spec.rb
:
require 'spec_helper'
describe FoosController, :type => :controller do
describe "GET redirect_to_baz" do
it "redirects to example.com with params" do
get :redirect_to_baz
expect(response).to redirect_to "http://example.com/?foo=1&bar=2&baz=3"
end
end
end
It works. However, if someone swaps the query string parameters (e.g. http://example.com/?bar=2&baz=3&foo=1
), the test fails.
What's the right way of testing this?
I would like to do something like:
expect(response).to redirect_to("http://example.com/", params: { foo: 1, bar: 2, baz: 3 })
I looked at the documentation and I tried searching for response.parameters
, but I didn't find anything like that. Even Hash#to_query
doesn't seem to solve this problem.
Any help would be greatly appreciated.
From the documentation, the expected redirect path can match a regex:
expect(response).to redirect_to %r(\Ahttp://example.com)
To verify the redirect location's query string seems a little bit more convoluted. You have access to the response's location, so you should be able to do this:
response.location
# => http://example.com?foo=1&bar=2&baz=3
You should be able to extract the querystring params like this:
redirect_params = Rack::Utils.parse_query(URI.parse(response.location).query)
# => {"foo"=>"1", "bar"=>"2", "baz"=>"3"}
And from that it should be straightforward to verify that the redirect params are correct:
expect(redirect_params).to eq("foo" => "1", "baz" => "3", "bar" => "2")
# => true
If you have to do this sort of logic more than once though, it would definitely be convenient to wrap it all up into a custom rspec matcher.
Are you able to use your route helpers rather than a plain string? If so, you can just pass hash params to a route helper and they will be converted to query string params:
root_url foo: 'bar', baz: 'quux'
=> "http://www.your-root.com/?baz=quux&foo=bar"
expect(response).to redirect_to(root_url(foo: 'bar', baz: 'quux'))
Does that help, or are you restricted to using strings rather than route helpers?
Another thought is that you could just assert directly on the values in the params hash rather than the url + query string, since the query string params will get serialized into the params hash...
I had a need for something similar and ended up with this:
Was able to write tests and not worry about query parameter ordering.
expect(response.location).to be_a_similar_url_to("http://example.com/?beta=gamma&alpha=delta")
Drop the following into ./spec/support/matchers/be_a_similar_url_to.rb
RSpec::Matchers.define :be_a_similar_url_to do |expected|
match do |actual|
expected_uri = URI.parse(expected)
actual_uri = URI.parse(actual)
expect(actual_uri.host).to eql(expected_uri.host)
expect(actual_uri.port).to eql(expected_uri.port)
expect(actual_uri.scheme).to eql(expected_uri.scheme)
expect(Rack::Utils.parse_nested_query(actual_uri.query)).to eql(Rack::Utils.parse_nested_query(expected_uri.query))
expect(actual_uri.fragment).to eql(expected_uri.fragment)
end
# optional
failure_message do |actual|
"expected that #{actual} would be a similar URL to #{expected}"
end
# optional
failure_message_when_negated do |actual|
"expected that #{actual} would not be a similar URL to #{expected}"
end
end