【问题标题】:Rails functional test: sending URL query parameters in POST requestRails 功能测试:在 POST 请求中发送 URL 查询参数
【发布时间】:2014-08-04 06:49:34
【问题描述】:

我在 Rails 功能测试中发送一个 POST 请求,如下所示:

post :create, collection: { name: 'New Collection' }

collection 按预期以 JSON 编码的表单数据形式发送。

我不知道如何向 URL 添加查询。 The documentation 说我可以访问请求对象并在发送之前对其进行修改。所以我尝试了这个:

@request.GET[:api_key] = 'my key'
post :create, collection: { name: 'New Collection' }

但是,:api_key 永远不会出现在服务器上的 request.GET 哈希中。 (不过,当我通过另一个 HTTP 客户端发送它时,它确实如此。)

【问题讨论】:

  • 我不知道它是否可以工作,但你试过post 'create?api_key=my_key', collection: { name: 'New Collection' }吗?
  • @MrYoshiji 不幸的是:“Minitest::UnexpectedError: ActionController::UrlGenerationError: 没有路由匹配”
  • 为什么同时使用查询字符串和正文参数?这似乎很奇怪。通常,POST 仅使用正文参数。
  • @BSeven 我想将我的 POST 正文作为一些非表单数据格式发送,例如 applicaton/xml、application/json 等。
  • 这不是很语义化。这应该在 Content-Type 标头中发送。除此之外,既然可以将其作为正文参数发送,为什么还要将其作为 URL 参数发送?

标签: ruby-on-rails functional-testing


【解决方案1】:

先说明一点背景知识:虽然一个请求不能同时是 GET 和 POST,但是在使用 POST 时,有nothing stopping你使用查询字符串和正文表单数据。您甚至可以在查询字符串and an empty body 中包含所有参数的 POST,尽管这听起来很不寻常。

Rails 支持这种情况,实际上您可以轻松地使用 POST 请求发送表单,并且仍然在表单的操作中进行查询。可以使用request.GET hashquery_string 的别名)访问查询,而使用request.POST hashrequest_parameters 的别名)的 POST 正文参数。 params 哈希实际上是从 combined GET and POST hashes 构造的。

但是,根据我的研究,Rails 似乎不支持在功能控制器测试中的 POST 请求中传递查询字符串。尽管我在任何文档或known issues on github 中都找不到任何关于此的内容,但源代码非常清晰。在以下文本中,我假设您使用的是 Rails 4。

为什么它不起作用

功能控制器测试的问题在于它们不使用真实的请求/响应,而是模拟 HTTP 握手:请求被模拟,其参数填充在适当的位置,并且给定的控制器操作被简单地调用为正常红宝石方法。所有这些都在action_controller/test_case classes 中完成。

事实证明,这种模拟不适用于您的特定情况,原因有两个:

  1. 运行测试时传入的参数是alwayshanded over eitherrequest_parameters,即使用post请求时的request.POST hash 发送到query_string(即request.GET)以获取get 测试请求。无法在一次测试运行期间同时设置这两个哈希值。

    这实际上是有道理的,因为功能测试中的getpost 等助手只接受一个参数散列,因此内部测试代码不知道如何将它们分成两个散列。

  2. 确实可以使用@request 变量在运行测试之前设置请求,但只能在一定程度上设置标头,例如。但是您不能设置请求的内部属性,因为在测试运行期间它们会被回收。回收完成here 并重置请求对象和底层机架请求对象的所有内部变量。因此,如果您尝试设置像 @request.GET[:api_key] = 'my key' 这样的请求 GET 参数,它不会产生任何影响,因为表示此哈希的内部变量将在回收期间被擦除。

解决方案/解决方法

  • 放弃功能测试并选择集成测试。集成测试allow to set the rack environment variables 与主要参数分开。以下集成测试通过了QUERY_STRING rack env 变量以及正常的 post body 参数,并且应该可以正常工作:

    class CollectionsTest < ActionDispatch::IntegrationTest
      test 'foo' do
        post collections_path, { collection: { name: 'New Collection' } }, 
                               { "QUERY_STRING" => "api_key=my_api_key" }
    
        # this proves that the parameters are recognized separately in the controller
        # (you can test this in you controller as well as here in the test):
        puts request.POST.inspect
        # => {"collection"=>{"name"=>"New Collection"}}
        puts request.GET.inspect
        # => {"api_key"=>"my_api_key"}
      end
    end
    

    您仍然可以在集成测试中使用功能测试中的大部分功能。例如。您可以使用 assigns 哈希在控制器中测试分配的实例变量。

    Rails 5 将 deprecate 功能控制器测试支持集成测试也支持转换参数,并且从 Rails 5.1 开始,这些功能测试支持将移出到单独的 gem .

  • 试用 Rails 5:虽然功能测试将被弃用,但它的源代码似乎在 rails master 和 e.g. 中是 heavily rewritten不再使用请求的回收。因此,您可以尝试一下,并尝试在测试设置期间设置请求的内部变量。不过我还没有测试过。

  • 当然,您始终可以尝试对功能测试进行猴子修补,以便它支持在测试中定义 query_stringrequest_parameters 哈希的单独参数。

我会走集成测试路线:)。

【讨论】:

    【解决方案2】:

    我假设控制器被命名为CollectionsController,它到create动作的路由是/collections(如果不是,你只需要修改下面的例子)

    我还假设您在请求规范中

    这应该可行:

    post '/collections?api_key=my_key', collection: { name: 'New Collection' }
    

    【讨论】:

    • “Minitest::UnexpectedError: ActionController::UrlGenerationError: 没有路由匹配”(这是 Rails 4.1/Minitest 5.1 btw)
    • 啊,好吧,我不知道 minitest。在 Rspec 中,您可以对控制器进行 3 种不同类型的测试。在其中之一“请求规范”中,您可以这样做,因为它通过 Rails 路由堆栈。这就是您在这里所需要的。那么minitest呢,有这种测试环境吗?
    【解决方案3】:

    post 的第二个参数是您将在控制器中收到的所有参数的哈希值。只需这样做:

    post :create, collection: { name: 'New Collection' }, more_params: 'stuff', and_so_on: 'things'
    

    这些参数将在控制器中可用:

    params[:and_so_on] == 'things'
    

    【讨论】:

    • 不幸的是,向post 添加更多参数只会将键添加到请求正文中的 JSON 对象。我需要添加到 URL 查询,例如http://../?key=value
    • 您如何检索控制器中的值?
    • 使用 request.GET 和 request.POST。我知道测试中的 post 参数将出现在服务器上的 params 哈希中,但是在这种情况下,我正在构建一个 API,无论它如何接受 api_key请求类型(GET、POST、PATCH 等)。
    【解决方案4】:

    你想发送一个 POST 请求:

    我在 Rails 功能测试中发送一个 POST 请求,如下所示:

    但您想从 GET 请求中检索数据:

    但是,:api_key 永远不会出现在服务器上的 request.GET 哈希中。

    一个请求不能同时是 GET 和 POST,如果您发送 POST 请求并在查询字符串中传递参数,那么您将在 POST 请求中获得这些参数值,GET 将没有任何东西。

    然后:

    @request.GET[:api_key] = 'my key'
    post :create, collection: { name: 'New Collection' }
    

    您正在修改请求上的 GET 值,但随后您实际上发送了一个 POST 请求,这意味着当调用 post 方法并将请求发送到服务器时,只有您在 POST 上发送的内容才可用.只需发送与 POST 请求捆绑的 api 密钥(可能在集合哈希中)

    【讨论】:

      【解决方案5】:

      在使用 RSpec (v3.4) 测试 POST 操作时,这也是一个问题。

      一种解决方法是模拟request.GETrequest.query_string 方法的返回值。

      it "should recognise a query parameter in post action" do
        allow(subject.request).to receive(:query_string).and_return("api_key=my%20key")  
        @params = {collection: { name: 'New Collection' }}
      
      
        expect(subject.request.query_string).to eq "api_key=my%20key"
      
        post :create, @params
      end
      

      【讨论】:

      • 我试过了,但没有用。对我有用的是存根 `query_parameters' 以返回我将在查询字符串中传递的参数的哈希值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-14
      • 2015-06-10
      • 2018-12-14
      • 2021-05-13
      • 1970-01-01
      相关资源
      最近更新 更多