问题描述
我正在这样的Rails功能测试中发送POST请求:
I'm sending a POST request in a Rails functional test like this:
post :create, collection: { name: 'New Collection' }
collection
被发送为JSON编码的表单数据,符合预期.
collection
gets sent as JSON-encoded form data, as expected.
我不知道如何在URL中添加查询. 文档说,我可以访问请求对象并在发送请求之前对其进行修改.所以我尝试了这个:
What I can't figure out is how to add a query to the URL. The documentation says that I can access the request object and modify it before it gets sent. So I tried this:
@request.GET[:api_key] = 'my key'
post :create, collection: { name: 'New Collection' }
但是,:api_key
永远不会出现在服务器上的request.GET
哈希中. (不过,当我通过另一个HTTP客户端发送它时也是如此.)
But, :api_key
never appears in the request.GET
hash on the server. (It does when I send it though another HTTP client, though.)
推荐答案
首先要弄清楚背景:尽管请求不能同时是GET和POST,但是有同时使用查询字符串和正文形式数据.您甚至可以在查询字符串中包含所有参数的POST,并留空身体,尽管听起来很不寻常.
A little background first to clarify things: although a request cannot be both GET and POST at the same time, there is nothing stopping you from using both the query string and body form data when using POST. You can even have a POST with all parameters in the query string and an empty body, though this sounds quite unusual.
Rails支持这种情况,实际上,您可以轻松地使用POST请求发送表单,并且仍然可以对表单进行查询.该查询将通过 request.GET
哈希(这是query_string
的别名),而POST正文使用 request.POST
哈希(request_parameters
的别名). params
哈希实际上是根据组合的和POST
哈希.
Rails supports this scenario and indeed you can easily send a form using a POST request and still have query in the form's action. The query will be accessible with request.GET
hash (which is an alias of query_string
), while the POST body params with the request.POST
hash (an alias of request_parameters
). The params
hash is actually constructed from the combined GET
and POST
hashes.
但是,从我的研究看来, Rails不支持在功能控制器测试中的POST请求中传递查询字符串.尽管我在任何文档中或 github上的已知问题,源代码非常清晰.在下面的文本中,我假设您使用Rails 4.
However, from my research it seems that Rails does not support passing query string in POST requests in functional controller tests. Although I could not find anything regarding this in any documentation or among known issues on github, the source code is quite clear. In the following text, I'm assuming that you use Rails 4.
功能性控制器测试的问题在于它们不使用实际的请求/响应,而是模拟HTTP握手:模拟请求,将其参数填充在适当的位置,并且将给定的控制器操作简单地称为正常操作红宝石方法.所有这些都是在 类.
The problem with functional controller tests is that they don't use real requests / responses but they simulate the HTTP handshake: the request is mocked up, its parameters filled in appropriate places and the given controller action is simply called as a normal ruby method. All of this is done in the action_controller/test_case
classes.
事实证明,由于以下两个原因,该模拟不适用于您的特定情况:
As it turns out, this simulation is not working in your particular case, due to two reasons:
-
运行测试时传递的参数总是 移交 两个到
request_parameters
,即使用post
请求时request.POST
哈希get测试请求,请选择或到query_string
(即request.GET
).在单个测试运行中无法同时设置这两个哈希值.
The parameters passed in when running the test are always handed over either to the
request_parameters
, i.e. therequest.POST
hash when using apost
request or to thequery_string
(i.e.request.GET
) forget
test requests. There is no way for both of these hashes to be set during a single test run.
这实际上与get
,post
等类似.在功能测试中,辅助函数仅接受一个参数散列,因此内部测试代码无法知道如何将它们分为两个散列.
This actually makes some sense as the get
, post
, etc. helpers in functional tests accept only a single hash of params so the internal test code cannot know how to separate them into the two hashes.
的确可以在使用@request
变量运行测试之前设置请求,但是例如,可以在一定程度上设置标头.但是您不能设置请求的内部属性,因为它们在测试运行期间被回收.回收是在此处完成的a>,它将重置请求对象和基础机架请求对象的所有内部变量.因此,如果尝试设置像@request.GET[:api_key] = 'my key'
这样的请求GET参数,则该参数将无效,因为代表该哈希的内部变量将在回收期间被擦除.
解决方案/解决方法
-
放弃功能测试,并选择集成测试.集成测试可以设置机架环境变量与主要参数分开.以下集成测试除了通过普通的柱体参数之外还通过了
QUERY_STRING
rack env变量,并且可以正常工作:Solutions / workarounds
Give up functional testing and choose integration tests instead. Integration tests allow to set the rack environment variables separately from the main parameters. The following integration test passes the
QUERY_STRING
rack env variable besides the normal post body params and should work flawlessly: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
哈希在控制器中测试分配的实例变量.You can still use most of the features from functional tests in your integration tests. E.g. you can test for assigned instance variables in the controller with the
assigns
hash.Rails 5将不赞成支持集成测试的功能控制器测试,并且从Rails 5.1开始,这些功能测试支持将移至单独的gem.
The transition argument is supported also by the fact that Rails 5 will deprecate functional controller tests in favor of integration testing and since Rails 5.1 these functional tests support will be moved out to a separate gem.
尝试使用Rails 5:尽管不建议使用功能测试,但其源代码似乎已经在Rails主目录中大量重写,例如请求的回收已不再使用.因此,您可以尝试在测试设置过程中尝试设置请求的内部变量.我还没有测试过.
Try Rails 5: although functional tests will be deprecated, its source code seems to have been heavily rewritten in the rails master and e.g. recycling of the request is not used any more. So you might give it a try and try to set the internal variables of the request during test setup. I have not tested it though.
当然,您总是可以尝试对功能测试进行猴子修补,以使其支持要在测试中定义的
query_string
和request_parameters
哈希的单独参数.Of course, you can always try to monkey-patch the functional test so that it supports separate params for the
query_string
andrequest_parameters
hashes to be defined in tests.我会去进行集成测试:).
I'd go the integration tests route :).
这篇关于Rails功能测试:在POST请求中发送URL查询参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!