How can I test only a part of the URL I am redirected to (using assert_redirected_to)?
In a functional test of my Rails app, I want t开发者_开发问答o test where I am redirected to. The expected URL points to an external resource (that means it's not part of my application).
The URL looks like this: https://my.url.com/foo?bar1=xyz&bar2=123
Unfortunately I can't predict the parameters, because they are generated by an external resource.*
However, the rest of the URL always stays the same:https://my.url.com/foo
I usually use assert_redirected_to
for this kind of test, but this expects the whole URL, including the parameters.
Can anyone think of another way to test that redirection but only check for the first part of the URL without the parameters?
(the URL is not in the assigns
Hash)
*(I make an API call to an application, which responses with the URL I shall redirect_to)
The http request commands like post
, get
etc. create an instance variable called @response
when they are called*. @response
itself contains a method called redirect_url
which stores the URL you have been redirected to (in case you have really been redirected).
Therefor, I can just use a normal assert_match
to compare a regular expression to @response.redirect_url
:
post :my_action_to_test
assert_response :redirect
assert_match /https:\/\/my.url.com\/foo/, @response.redirect_url
*(actually, these http methods just use the private method process
, which creates the @response
variable).
New and improved! Rails offers regex matching in assert_redirected_to as of 6.1.
You can just call
assert_redirected_to %r(\Ahttp://example.org/funky_cold_medina/\d+)
Rspec Rails just delegates to this method, so even though it is not documented, it works too.
subject { post some_path, params: { chunky_bacon: true} }
it "puts the lotion on itself" do
expect(subject).to redirect_to( %r(/catch_the_puppy/\d+) )
end
Two quick thoughts on this:
1) If your functional test actually connects to the external application, why not just get the params out of it as you would normally and test that the redirect occurs properly?
2) If your functional test does not actually connect to the external application, then you're faking it anyway, so I would just skip testing the redirect URL and just test for a redirect with assert_response :redirect. Or, create a mock that returns the URL for redirection as if it were the external app, but do it in such a way that you can get the params out of it.
That said, don't get so carried away with tests that you feel you have to cover every single possible scenario.
How about this? It wraps assert_redirected_to
to allow a Regexp
as the first argument. This won't work if you try to match the Regexp
to a Hash
-- only a String
, though. Doing that would take a bit more work.
ActionController::TestCase.class_eval do
old_assert_redirected_to = method(:assert_redirected_to)
define_method(:assert_redirected_to) do |*args|
if args.[0].kind_of?(Regexp)
assert_response(:redirect, args[1])
assert args[0] === @response.redirected_to
else
old_assert_redirected_to.bind(self).call(*args)
end
end
end
I use the following method to ignore any query string parameters. It is based off of the official implementation of assert_redirected_to
# ignores any query string params eg. notice or alert messages for flash
def custom_assert_redirected_to(path)
assert_response :redirect
if path === Regexp
url = path
else
url = ActionController::Redirecting._compute_redirect_to_location(@request, path)
end
assert_equal url, @response.location.split("?").first
end
精彩评论