如何在Rails功能测试中发送原始帖子数据?

时间:2010-01-20 18:42:30

标签: ruby-on-rails json testing

我希望将原始发布数据(例如,未配置的JSON)发送到我的某个控制器进行测试:

class LegacyOrderUpdateControllerTest < ActionController::TestCase
  test "sending json" do
    post :index, '{"foo":"bar", "bool":true}'
  end
end

但这会给我一个NoMethodError: undefined method `symbolize_keys' for #<String:0x00000102cb6080>错误。

ActionController::TestCase中发送原始发布数据的正确方法是什么?

以下是一些控制器代码:

def index
  post_data = request.body.read
  req = JSON.parse(post_data)
end

13 个答案:

答案 0 :(得分:55)

我今天遇到了同样的问题并找到了解决方案。

test_helper.rbActiveSupport::TestCase中定义以下方法:

def raw_post(action, params, body)
  @request.env['RAW_POST_DATA'] = body
  response = post(action, params)
  @request.env.delete('RAW_POST_DATA')
  response
end

在功能测试中,像post方法一样使用它,但将原始帖子体作为第三个参数传递。

class LegacyOrderUpdateControllerTest < ActionController::TestCase
  test "sending json" do
    raw_post :index, {}, {:foo => "bar", :bool => true}.to_json
  end
end

我在使用

读取原始帖子时在Rails 2.3.4上测试了这个
request.raw_post

而不是

request.body.read

如果您查看source code,您会看到raw_post只是在request.body.read env哈希中检查RAW_POST_DATA request

答案 1 :(得分:17)

我实际上只添加了一行解决了同样的问题 在模拟rspec post请求之前。你做什么 是填充&#34; RAW_POST_DATA&#34;。我试图删除 帖子上的属性var:create,但如果我这样做, 它没有找到行动。

这是我的解决方案。

def do_create(attributes)
  request.env['RAW_POST_DATA'] = attributes.to_json
  post :create, attributes
end 

在控制器中,您需要读取JSON的代码是 与此类似的东西

  @property = Property.new(JSON.parse(request.body.read))

答案 2 :(得分:9)

查看运行测试的堆栈跟踪,您可以获得对请求准备的更多控制: ActionDispatch :: Integration :: RequestHelpers.post =&gt; ActionDispatch::Integration::Session.process =&gt; Rack::Test::Session.env_for

您可以将json字符串传递为:params并指定内容类型“application / json”。在其他情况下,内容类型将设置为“application / x-www-form-urlencoded”,您的json将被正确解析。

所以你需要的是指定“CONTENT_TYPE”:

post :index, '{"foo":"bar", "bool":true}', "CONTENT_TYPE" => 'application/json'

答案 3 :(得分:5)

对于那些使用Rails5 +集成测试的人来说,(未记录的)方法是在params参数中传递一个字符串,所以:

post '/path', params: raw_body, headers: { 'Content-Type' => 'application/json' }

答案 4 :(得分:4)

如果您正在使用RSpec(&gt; = 2.12.0)并编写请求规范,则包含的模块为ActionDispatch::Integration::Runner。如果您查看源代码,可以注意到post方法调用process接受rack_env参数。

所有这些意味着您只需在规范中执行以下操作:

#spec/requests/articles_spec.rb

post '/articles', {}, {'RAW_POST_DATA' => 'something'}

在控制器中:

#app/controllers/articles_controller.rb

def create
  puts request.body.read
end

答案 5 :(得分:4)

Rails 5的版本:

post :create, body: '{"foo": "bar", "bool": true}'

请参阅here - body字符串参数被视为原始请求正文。

答案 6 :(得分:1)

使用Rails 4,我希望这样做来测试正在发布到控制器的原始xml的处理。我能够通过只为帖子提供字符串来实现:

raw_xml = File.read("my_raw.xml")
post :message, raw_xml, format: :xml

我相信如果提供的参数是一个字符串,它只是作为正文传递给控制器​​。

答案 7 :(得分:0)

post方法需要名称 - 值对的散列,因此您需要执行以下操作:

post :index, :data => '{"foo":"bar", "bool":true}'

然后,在您的控制器中,获取要解析的数据:

post_data = params[:data]

答案 8 :(得分:0)

从Rails 4.1.5开始,这是唯一对我有用的东西:

class LegacyOrderUpdateControllerTest < ActionController::TestCase
  def setup
    @request.headers["Content-Type"] = 'application/json'
  end

  test "sending json" do
    post :index, '{"foo":"bar", "bool":true}'.to_json, { account_id: 5, order_id: 10 }
  end
end

为/ accounts / 5 / orders / 10 / items的网址。这将获得url params以及JSON正文。当然,如果没有嵌入订单,那么你可以省去params hash。

class LegacyOrderUpdateControllerTest < ActionController::TestCase
  def setup
    @request.headers["Content-Type"] = 'application/json'
  end

  test "sending json" do
    post :index, '{"foo":"bar", "bool":true}'.to_json
  end
end

答案 9 :(得分:0)

在rails中,5.1在执行需要正文中的数据的删除请求时,以下工作:

delete your_app_url, as: :json, env: {
   "RAW_POST_DATA" =>  {"a_key" => "a_value"}.to_json
}

注意:这仅在进行集成测试时有效。

答案 10 :(得分:0)

我一直在寻找如何在集成测试(Rails 5.1)中发布原始JSON内容的方法。我想我的解决方案在这种情况下也可能会有所帮助。 我查看了post方法的文档和源代码:https://api.rubyonrails.org/v5.1/classes/ActionDispatch/Integration/RequestHelpers.html#method-i-post

这将我引向process方法以获取更多详细信息:https://api.rubyonrails.org/v5.1/classes/ActionDispatch/Integration/Session.html#method-i-process

由于这一点,我终于找到了process以及post方法所接受的参数。 这是我最终的解决方案:

post my_url, params: nil, headers: nil, env: {'RAW_POST_DATA' => my_body_content}, as: :json

答案 11 :(得分:0)

在 Rails 4(至少在 4.2.11.3)中,没有简单的方法来测试使用 json 的控制器(功能测试)。为了在正在运行的服务器中解析 json,ActionDispatch::ParamsParser middleware 负责。控制器测试虽然依赖于 Rack,但它无法解析 json to this day(不是它应该的)。

你可以这样做:

post :create, body_params.to_json

或:

post :update, body_parmas.to_json, url_params

但是 body_params 在控制器中无法通过 params 访问。你必须做JSON.parse(request.body.read)。所以唯一想到的是:

post :update, url_params.merge(body_params)

也就是说,在测试中通过参数 (application/x-www-form-urlencoded) 传递所有内容。在生产中,主体将被 ActionDispatch::ParamsParser 解析为相同的效果。除了你的数字变成字符串(可能更多):

# test/controllers/post_controller_test.rb
post :update, {id: 1, n: 2}

# app/controller/posts_controller.rb
def update
    p params  # tests:
              # {"id"=>"1", "n" => "2", "controller"=>"posts", "action"=>"update"}
              # production
              # {"id"=>"1", "n" => 2, "controller"=>"posts", "action"=>"update"}
end

如果你愿意自己解析控制器中的 json,你可以这样做:

# test/controllers/post_controller_test.rb
post_json :update, {n: 2}.to_json, {id: 1}

# app/controller/posts_controller.rb
def update
    p JSON.parse(request.body.read)  # {"id"=>"1", "n" => 2, "controller"=>"posts", "action"=>"update"}
end

答案 12 :(得分:-5)

post :index, {:foo=> 'bar', :bool => 'true'}