RSpec测试重定向到带有GET参数的URL

31
假设我有一个名为 FoosController 的控制器,并且其中有一个名为 redirect_to_baz 的方法。
class FoosController < ApplicationController

  def redirect_to_baz
    redirect_to 'http://example.com/?foo=1&bar=2&baz=3'
  end

end

我正在使用 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

这是有效的。然而,如果有人交换查询字符串参数(例如http://example.com/?bar=2&baz=3&foo=1),则测试将失败。

如何正确测试?

我想做类似这样的事情:

expect(response).to redirect_to("http://example.com/", params: { foo: 1, bar: 2, baz: 3 })

我查看了文档,并尝试搜索response.parameters,但我没有找到类似的内容。即使是Hash#to_query也似乎无法解决这个问题。
非常感谢您的帮助。
3个回答

41

根据文档,预期的重定向路径可以匹配一个正则表达式:

expect(response).to redirect_to %r(\Ahttp://example.com)

验证重定向位置的查询字符串似乎有点复杂。您可以访问响应的位置,因此应该能够执行此操作:

response.location
# => http://example.com?foo=1&bar=2&baz=3

你应该能够像这样提取查询字符串参数:

redirect_params = Rack::Utils.parse_query(URI.parse(response.location).query)
# => {"foo"=>"1", "bar"=>"2", "baz"=>"3"}

从中可以轻松验证重定向参数是否正确:

expect(redirect_params).to eq("foo" => "1", "baz" => "3", "bar" => "2")
# => true

如果您需要多次执行此类逻辑,将其全部封装成自定义的RSpec匹配器肯定会很方便。


3
expect(response).to redirect_to(A_REGEX) works for Rails 4 only. For Rails 3, your best bet is probably expect(response.location).to match(THE_REGEX) - Isaac Betesh

9

您能使用路由助手而不是纯字符串吗?如果可以,您只需向路由助手传递哈希参数,它们将被转换为查询字符串参数:

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'))

这有帮助吗?或者你只能使用字符串而不是路由helper?

另一个想法是,你可以直接对params哈希中的值进行断言,而不是针对url +查询字符串进行断言,因为查询字符串参数将被序列化到params哈希中...


这里存在一个问题,即参数的顺序可能会改变;"http://example.com/?foo=bar&baz=quux" 和 "http://example.com/?baz=quux&foo=bar" 应该是等效的,但这种期望只能匹配其中一个。 - TimP
如果使用路由助手构建URL,则顺序会被规范化,不会产生影响。root_url foo: 'bar', baz: 'quux'root_url baz: 'quux', foo: 'bar' 都会返回 "http://www.example.com/?baz=quux&foo=bar" - Cade
是的,它们都返回相同的URL,但这是问题所在,理想情况下,我们希望重定向到http://www.your-root.com/?baz=quux&foo=barhttp://www.your-root.com/?foo=bar&baz=quux中的任一一个来通过测试,但只有一个能够。 - TimP

6
我需要类似的东西,最终得到了这个:

能够编写测试而不用担心查询参数的顺序。

expect(response.location).to be_a_similar_url_to("http://example.com/?beta=gamma&alpha=delta")

将以下内容放入./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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接